package com.pactera.asmp.server.service;

import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.pactera.asmp.server.common.bug.IBugHandler;
import com.pactera.asmp.server.common.bug.impl.AbstractBugHandler;
import com.pactera.asmp.server.common.constants.*;
import com.pactera.asmp.server.common.exception.CustomException;
import com.pactera.asmp.server.common.utils.ExcelUtil;
import com.pactera.asmp.server.common.utils.ValidateUtil;
import com.pactera.asmp.server.common.websocket.WebSocketServerScreen;
import com.pactera.asmp.server.dao.*;
import com.pactera.asmp.server.entity.*;
import com.pactera.asmp.server.job.BaseJob;
import com.pactera.asmp.server.job.dispatch.impl.distribute.DistributeManager;
import com.pactera.asmp.server.job.dispatch.impl.feedback.FeedbackManager;
import com.pactera.asmp.server.job.dispatch.impl.task.TaskManager;
import com.pactera.asmp.server.pdf.PdfProjectTestReport;
import com.pactera.asmp.server.pojo.*;
import com.pactera.asmp.server.utils.AssertUtil;
import com.pactera.asmp.server.utils.DateUtil;
import com.pactera.asmp.server.utils.MinioUtils;
import com.pactera.asmp.server.utils.PageDataResult;
import com.pactera.asmp.server.vo.*;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.quartz.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @ProjectName: asmp-server
 * @Package com.pactera.asmp.server.service
 * @ClassName: TestTaskServiceImpl
 * @Description: TestTaskService
 * @Date 2020/08/04 13:56
 * @Author lvzhiqiang
 * @Version 1.0
 */

@Service
public class TaskServiceImpl implements TaskService {

    private static final Logger logger = LoggerFactory.getLogger(TaskServiceImpl.class);
    public static final String TASKID_NOTNULL = "任务ID不允许为空";
    public static final String DUID_NOTNULL ="Duid not null";
    @Autowired
    private TaskMapper taskMapper;
    @Autowired
    private DeviceInfoMapper deviceInfoMapper;

    @Autowired
    private IScriptInfoServcie iScriptInfoServcie;


    @Autowired
    private IDeviceServcie iDeviceServcie;

    @Autowired
    private ITestReportService iTestReportService;

    @Autowired
    private BaseJob autoTestJobV2;

    @Autowired
    private SubtaskMapper subtaskMapper;

    @Autowired
    private FeedbackManager feedbackManager;

    @Autowired
    private TaskManager taskManager;

    @Autowired
    @Lazy
    private ScreenDataService screenDataService;

    @Autowired
    private DeviceInfoMapper mDeviceInfoMapper;

    @Autowired
    private ProjectMapper projectMapper;

    @Autowired
    private ProjectModuleMapper projectModuleMapper;

    @Autowired
    private ScriptInfoMapper scriptInfoMapper;

    @Autowired
    @Qualifier("scheduler")
    private Scheduler scheduler;
    private String JOB_CLASS_NAME = "com.pactera.asmp.server.job.AutoTestJobV2";

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Autowired
    private MasterComputerMapper masterComputerMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private TestReportErrorVideoInfoMapper testReportErrorVideoInfoMapper;

    @Autowired
    private TestReportApplicationResponseTimeMapper testReportApplicationResponseTimeMapper;

    @Autowired
    private ProductionLineHistoryMapper productionLineHistoryMapper;

    @Autowired
    private ProjectProductionConfigMapper projectProductionConfigMapper;

    @Autowired
    private TaskPackageInfoMapper taskPackageInfoMapper;

    @Autowired
    private TaskColdHotStartupLogMapper taskColdHotStartupLogMapper;

    @Value("${minio.url}")
    private String url;

    @Value("${minio.bucketName}")
    private String buckName;

    @PostConstruct
    public void init(){
        taskManager.setTaskService(this);
    }

    /**
     * 定时任务加载, 无需加载，框架已实现加载
     */
   // @PostConstruct
    public void initJob(){
        TaskSearchDTO taskSearchDTO = new TaskSearchDTO();
        taskSearchDTO.setStatus(0);
        List<TaskDTO> list = taskMapper.getTaskList(taskSearchDTO);
        if(CollectionUtils.isEmpty(list)){
            return;
        }
        for(TaskDTO taskDTO:list){
            if(taskDTO.getCycleType() == 1){
                try {
                    Date createTime = Date.from(taskDTO.getCreateTime().atZone(ZoneId.systemDefault()).toInstant());
                    long excTime = createTime.getTime() + Long.valueOf(taskDTO.getCron());
                    if(taskDTO.getExecTime()!=null) {
                        if (taskDTO.getExecTime().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli() >= (excTime - 1000)) {
                            //已执行过
                            continue;
                        }
                    }
                    if (System.currentTimeMillis()>= excTime) {
                        taskDTO.setCycleType(2);//如果超过时间立即执行
                    }
                    _createJob(taskDTO);
                }catch (Exception e){
                    logger.error("error ",e);
                }
            }else if(taskDTO.getCycleType() == 0){
                LocalDateTime now = LocalDateTime.now();
                if(now.isAfter(taskDTO.getExeStartTime())
                        && now.isBefore(taskDTO.getExeEndTime())){
                    try {
                        _createJob(taskDTO);
                    } catch (Exception e) {
                        logger.error("error ",e);
                    }
                }
            }
        }
    }

    @Override
    public PageDataResult getTaskList(TaskSearchDTO taskSearchDTO) {
        PageDataResult pageDataResult = new PageDataResult();
        if (null == taskSearchDTO.getPageNum() || taskSearchDTO.getPageNum().intValue() == 0) {
            taskSearchDTO.setPageNum(1);
        }
        if (null == taskSearchDTO.getPageSize() || taskSearchDTO.getPageSize().intValue() == 0) {
            taskSearchDTO.setPageSize(10);
        }

        if(taskSearchDTO.getPageNum().intValue() < 0 || taskSearchDTO.getPageSize().intValue() < 0) {
            pageDataResult.setTotals(0);
            pageDataResult.setList(Lists.newArrayList());
            return pageDataResult;
        }
        taskSearchDTO.setProjectIds(userService.getRelatedProject());
        PageHelper.startPage(taskSearchDTO.getPageNum(), taskSearchDTO.getPageSize());
        List<TaskDTO> list = taskMapper.getTaskList(taskSearchDTO);
        list.stream().forEach(taskDTO -> {
            // 0 定期执行， 1，延时执行 获取子任务
            if(taskDTO.getCycleType() == 0 || taskDTO.getCycleType() == 1) {
                List<TaskDTO> subTaskDTOs = taskMapper.getTaskList(new TaskSearchDTO() {{setParentId(taskDTO.getId());}});
                subTaskDTOs = subTaskDTOs.stream().sorted(Comparator.comparing(TaskDTO::getId)).collect(Collectors.toList());
                taskDTO.setSubTaskDTOs(subTaskDTOs);
            }
            // 0 定期执行， 计算执行次数
            if(taskDTO.getCycleType() == 0 && taskDTO.getCreateTime().isBefore(taskDTO.getExeEndTime())) {
                int cnt = 0;
                CronExpression cronExpression;
                try {
                    LocalDateTime startTime;
                    if(taskDTO.getCreateTime().isBefore(taskDTO.getExeStartTime())) {
                        startTime = taskDTO.getExeStartTime().plus(-1L, ChronoUnit.MILLIS);
                    }else {
                        startTime = taskDTO.getCreateTime().plus(-1L, ChronoUnit.MILLIS);
                    }
                    cronExpression = new CronExpression(taskDTO.getCron());
                    LocalDateTime execTime = DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(DateUtil.toDate(startTime)));
                    while(taskDTO.getExeEndTime().isAfter(execTime) || taskDTO.getExeEndTime().equals(execTime)) {
                        cnt++ ;
                        execTime = DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(DateUtil.toDate(execTime)));
                    }
                } catch (ParseException e) {
                    logger.error(e.getMessage());
                }
                taskDTO.setQrtzExeTimes(cnt);
            }
            if(null != taskDTO.getProductionLineHistoryId()) {
                taskDTO.setProductionLineHistory(productionLineHistoryMapper.selectByPrimaryKey(taskDTO.getProductionLineHistoryId()));
            }
        });
        PageInfo<TaskDTO> pageInfo = new PageInfo<>(list);
        pageDataResult.setTotals(Long.valueOf(pageInfo.getTotal()).intValue());
        pageDataResult.setList(list);
        return pageDataResult;
    }

    @Override
    public List<TaskDTO> getTaskListByMasterComputerId(Integer masterComputerId, Integer projectId, Date startTime, Date endTime) {
        List<Integer> projectIds = userService.getRelatedProject();
        List<ProjectMasterDeviceVo> projectMasterDeviceVos = masterComputerMapper.listBindByProjectId(projectId, masterComputerId, projectIds);
        if(CollectionUtils.isEmpty(projectMasterDeviceVos)) {
            logger.error("projectMasterDeviceVos not exist");
            return Lists.newArrayList();
        }
        List<DeviceInfoWithProject> deviceInfos = projectMasterDeviceVos.get(0).getDeviceInfos();
        if(CollectionUtils.isEmpty(deviceInfos)) {
            logger.error("device not exist");
            return Lists.newArrayList();
        }
        List<TaskDTO> historyList = taskMapper.queryHistoryByDuids(deviceInfos.stream().map(DeviceInfoWithProject::getDuid).distinct().collect(Collectors.toList()), projectId);
        if(CollectionUtils.isEmpty(historyList)) {
            logger.error("duid not exist");
            return Lists.newArrayList();
        }
        TaskSearchDTO taskSearchDTO =new TaskSearchDTO();
        taskSearchDTO.setIds(historyList.stream().map(TaskDTO::getParentId).distinct().collect(Collectors.toList()));
        taskSearchDTO.setProjectIds(projectIds);
        List<TaskDTO> list = taskMapper.getTaskList(taskSearchDTO);
        if(null != startTime && null != endTime) {
            LocalDateTime startLocalDateTime = DateUtil.toLocalDateTime(startTime);
            LocalDateTime endLocalDateTime = DateUtil.toLocalDateTime(endTime);
            list = list.stream().filter(taskDTO -> {
                //0 定期执行， 1，延时执行， 2， 立刻执行
                switch(taskDTO.getCycleType()) {
                case 0:
                    CronExpression cronExpression;
                    try {
                        if(null == taskDTO.getExeStartTime() || null == taskDTO.getExeEndTime()
                                || startLocalDateTime.isAfter(taskDTO.getExeEndTime())
                                || endLocalDateTime.isBefore(taskDTO.getExeStartTime())) {
                            return false;
                        }
                        // 获取当时时间与执行时间的交集
                        LocalDateTime startCompareTime = startLocalDateTime;
                        LocalDateTime endCompareTime = endLocalDateTime;
                        if(startLocalDateTime.isBefore(taskDTO.getExeStartTime())) {
                            startCompareTime = taskDTO.getExeStartTime();
                        }
                        if(endLocalDateTime.isAfter(taskDTO.getExeEndTime())) {
                            endCompareTime = taskDTO.getExeEndTime();
                        }
                        if(startCompareTime.isAfter(endCompareTime)) {
                            return false;
                        }
                        startCompareTime = startCompareTime.plus(-1L, ChronoUnit.MILLIS);
                        cronExpression = new CronExpression(taskDTO.getCron());
                        LocalDateTime execTime = DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(DateUtil.toDate(startCompareTime)));
                        List<LocalDateTime> execTimes = Lists.newArrayList();
                        while(endCompareTime.isAfter(execTime) || endCompareTime.equals(execTime)) {
                            execTimes.add(execTime);
                            execTime = DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(DateUtil.toDate(execTime)));
                        }
                        if(CollectionUtils.isEmpty(execTimes)) {
                            return false;
                        }
                        taskDTO.setActExecTime(execTimes.stream().map(time -> DateUtil.format(time, DateUtil.DATE_FORMAT_HHMISS)).collect(Collectors.joining(",")));
                        return true;
                    } catch (ParseException e) {
                        logger.error(e.getMessage());
                    }
                    return false;
                case 1:
                    if(StringUtils.isNotEmpty(taskDTO.getCron())) {
                        LocalDateTime execTime = taskDTO.getCreateTime().plus(Long.parseLong(taskDTO.getCron()), ChronoUnit.MILLIS);
                        taskDTO.setActExecTime(DateUtil.format(execTime, DateUtil.DATE_FORMAT_HHMISS));
                        return (startLocalDateTime.isBefore(execTime) || startLocalDateTime.equals(execTime))
                                && (endLocalDateTime.isAfter(execTime) || endLocalDateTime.equals(execTime));
                    }
                    if(null != taskDTO.getExeStartTime()) {
                        taskDTO.setActExecTime(DateUtil.format(taskDTO.getExeStartTime(), DateUtil.DATE_FORMAT_HHMISS));
                        return (startLocalDateTime.isBefore(taskDTO.getExeStartTime()) || startLocalDateTime.equals(taskDTO.getExeStartTime()))
                                && (endLocalDateTime.isAfter(taskDTO.getExeStartTime()) || endLocalDateTime.equals(taskDTO.getExeStartTime()));
                    }
                    return false;
                case 2:
                    LocalDateTime execTime = taskDTO.getCreateTime();
                    taskDTO.setActExecTime(DateUtil.format(execTime, DateUtil.DATE_FORMAT_HHMISS));
                    return (startLocalDateTime.isBefore(execTime) || startLocalDateTime.equals(execTime))
                            && (endLocalDateTime.isAfter(execTime) || endLocalDateTime.equals(execTime));
                default:
                    return false;
                }
            }).sorted(Comparator.comparing(TaskDTO::getActExecTime)).collect(Collectors.toList());
        }
        return list;
    }

    @Override
    public TaskDTO getTaskById(Integer taskId) {
        TaskDTO task = taskMapper.getTaskById(taskId);
        if(task != null && task.getExecTime() != null){
            task.setExecTimeFormat(DateTimeFormatter.ofPattern(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS).format(task.getExecTime()));
        }
        return task;
    }

    public TaskDTO queryTaskById(Integer taskId){
        TaskDTO task = taskMapper.getTaskById(taskId);
        if(task != null && task.getExecTime() != null){
            task.setExecTimeFormat(DateTimeFormatter.ofPattern(DateUtil.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS).format(task.getExecTime()));
        }
        if(task == null){
            return null;
        }
        if(task.getCycleType() == 2){
            //立即执行类型
            return task;
        }
        TaskSearchDTO taskSearchDTO = new TaskSearchDTO();
        taskSearchDTO.setParentId(taskId);
        List<TaskDTO> taskList = taskMapper.getTaskList(taskSearchDTO);
        if(CollectionUtils.isEmpty(taskList)){
            return null;
        }
        return taskList.get(0);
    }

    @Override
    public TaskDTO getTaskHistoryById(Integer taskId) {
        return taskMapper.getTaskHistoryById(taskId);
    }

    @Override
    public List<ScriptInfo2> getTaskScriptInfo(Integer taskId) {
        TaskDTO taskById = taskMapper.getTaskById(taskId);

        if (taskById == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
        String scriptIds = taskById.getScriptIds();
        if (StringUtils.isEmpty(scriptIds)) throw new CustomException(ResultCode.DATA_IS_WRONG);

        String[] idsArr = scriptIds.split(",");
        List<Integer> ids = Arrays.stream(idsArr)
                .map(s -> Integer.parseInt(s.trim()))
                .collect(Collectors.toList());
        return iScriptInfoServcie.selectScriptByScriptIds(ids);
    }

    private void checkNewTaskScheduleParam(TaskDTO taskDTO) {
        AssertUtil.notNull(taskDTO, "参数异常");
        AssertUtil.hasText(taskDTO.getName(), "任务名称不允许为空");
        AssertUtil.isTrue(taskDTO.getName().matches(Constants.REGEXP_FIRST_LAST_NOT_SPACE), ResultCode.PARAM_TYPE_BIND_ERROR.message());
        AssertUtil.notNull(taskDTO.getType(), "任务类型不允许为空");
        AssertUtil.notNull(taskDTO.getCycleType(), "执行方式不允许为空");
        if(taskDTO.getCycleType() != 2){
            AssertUtil.hasText(taskDTO.getCron(), "计划时间不允许为空");

            if(taskDTO.getCycleType() == 0) {
                AssertUtil.notNull(taskDTO.getExeStartTime(), "开始时间不允许为空");
                AssertUtil.notNull(taskDTO.getExeEndTime(), "结束时间不允许为空");
                AssertUtil.isTrue(CronExpression.isValidExpression(taskDTO.getCron()), "计划时间格式不正确");

                // 校验是否有可执行时间
                CronExpression cronExpression;
                try {
                    cronExpression = new CronExpression(taskDTO.getCron());
                    LocalDateTime startTime = taskDTO.getExeStartTime().plus(-1L, ChronoUnit.MILLIS);
                    if(startTime.isBefore(LocalDateTime.now())) {
                        startTime = LocalDateTime.now();
                    }
                    LocalDateTime execTime = DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(DateUtil.toDate(startTime)));
                    AssertUtil.isTrue(!execTime.isAfter(taskDTO.getExeEndTime()), "该定时任务无可执行时间");
                } catch (ParseException e) {
                    logger.error(e.getMessage());
                }
            }else if(taskDTO.getCycleType() == 1) {
                AssertUtil.isTrue(ValidateUtil.validatePositiveInt(taskDTO.getCron()), "计划时间格式不正确");
            }
//
//            if(!DateUtil.compareDate(taskDTO.getExeStartTime(),taskDTO.getExeEndTime())
//                || !DateUtil.compareDate(DateUtil.now(),taskDTO.getExeEndTime()))  throw new CustomException(ResultCode.BUSINESS_NO_DATETIME);

        }
        if(taskDTO.getType() != 3){
            AssertUtil.hasText(taskDTO.getScriptIds(), "脚本ID不允许为空");
            AssertUtil.notNull(taskDTO.getExecType(), "实施类型不允许为空");
        }else {
            AssertUtil.hasText(taskDTO.getPackages(), "包名不允许为空");
            AssertUtil.notNull(taskDTO.getMonkeyExeNumber(), "monkey执行次数不允许为空");
        }
        AssertUtil.notNull(taskDTO.getProjectId(), "项目ID不允许为空");
        AssertUtil.notNull(taskDTO.getExeNumber(), "执行次数不允许为空");
    }

    @Override
    @Transactional
    public ResponseResult newTaskSchedule(TaskDTO taskDTO) throws Exception {
        this.checkNewTaskScheduleParam(taskDTO);
        // 校验名称是否重复
        if(!checkTaskNameExist(taskDTO.getProjectId(), taskDTO.getName(), null)) {
            throw new CustomException(ResultCode.BUSINESS_TESTTASK_EXISTED);
        }
        // 校验项目与脚本是否匹配
        if(!checkProjectIdMatchScriptIdStrs(taskDTO.getProjectId(), taskDTO.getScriptIds())) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_UPDATE_ERR);
        }
        // 校验脚本是否同为性能测试脚本或者同为非性能测试脚本
        if(!checkScriptPerformanceTypeSame(taskDTO.getScriptIds())) {
            throw new CustomException(ResultCode.BUSINESS_SCRIPT_PERFORMANCE_TYPE_NOT_SAME);
        }
        User currUser = (User) SecurityUtils.getSubject().getPrincipal();
        if (currUser == null) throw new CustomException(ResultCode.PERMISSION_EXPIRE);

        Integer userId = currUser.getId();
        taskDTO.setId(null);
        taskDTO.setStatus(0);
        taskDTO.setExecType(taskDTO.getExecType()==null?ExeType.DISTRIBUTE_TYPE.getValue():taskDTO.getExecType());
        taskDTO.setParentId(null);
        taskDTO.setUserId(userId);

        taskDTO.setExePlanTimes(1);
        if(taskDTO.getCycleType() == 0){
   //         taskDTO.setExePlanTimes(CronCountUtil.count(taskDTO.getExeStartTime(),taskDTO.getExeEndTime(),taskDTO.getCron()));
        }

        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            this._newTask(taskDTO);
    
            List<String> duids = Arrays.asList(taskDTO.getDuid());
            taskManager.prepareTaskDevice(duids, taskDTO);
    
            this._createJob(taskDTO);
            transactionManager.commit(status);
        } catch (Exception e) {
            logger.error(e.getMessage());
            transactionManager.rollback(status);
            throw new CustomException(ResultCode.SYSTEM_INNER_ERROR);
        }
        return ResponseResult.defaultSuccess(taskDTO.getId());
    }

    @Override
    public ResponseResult newTaskSchedule(TaskCreateRequest taskReq) throws Exception {
        TaskDTO taskDTO = taskReq.getTaskDTO();
        this.checkNewTaskScheduleParam(taskDTO);
        // 设备不能为空
        if(CollectionUtils.isEmpty(taskReq.getDuidList())) {
            throw new CustomException(ResultCode.PARAM_IS_BLANK);
        }
        if(taskReq.getDuidList().stream().allMatch(duidVo -> StringUtils.isEmpty(duidVo.getDuid()))) {
            throw new CustomException(ResultCode.PARAM_IS_BLANK);
        }
        // 校验名称是否重复
        if(!checkTaskNameExist(taskDTO.getProjectId(), taskDTO.getName(), null)) {
            throw new CustomException(ResultCode.BUSINESS_TESTTASK_EXISTED);
        }
        // 校验项目与脚本是否匹配
        if(taskDTO.getType() != 3 && taskDTO.getType() != 4) {
            if(!checkProjectIdMatchScriptIdStrs(taskDTO.getProjectId(), taskDTO.getScriptIds())) {
                throw new CustomException(ResultCode.BUSINESS_PROJECT_SCRIPT_NOT_MATCH_ERR);
            }
            // 校验脚本是否同为性能测试脚本或者同为非性能测试脚本
            if(!checkScriptPerformanceTypeSame(taskDTO.getScriptIds())) {
                throw new CustomException(ResultCode.BUSINESS_SCRIPT_PERFORMANCE_TYPE_NOT_SAME);
            }
        }else if(taskDTO.getType() != 4){
            taskDTO.setScriptIds("");
        }
        // 判断设备是否在线
        if(checkDeviceOnline(taskReq.getDuidList())) {
            throw new CustomException(ResultCode.BUSINESS_ALL_DEVICE_OFFLINE_ERR);
        }
        Integer userId = 0;
        // 流水线自动测试任务，userId设置为0
        if(taskDTO.getType().intValue() != 2) {
            User currUser = (User) SecurityUtils.getSubject().getPrincipal();
            if (currUser == null) throw new CustomException(ResultCode.PERMISSION_EXPIRE);
            userId = currUser.getId();
        }
        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            createTask(taskReq, taskDTO, userId);
            transactionManager.commit(status);
        } catch (Exception e) {
            logger.error("createTask: ",e);
            transactionManager.rollback(status);
            throw new CustomException(ResultCode.SYSTEM_INNER_ERROR);
        }
        return ResponseResult.defaultSuccess(taskDTO.getId());
    }

    public void createTask (TaskCreateRequest taskReq, TaskDTO taskDTO, Integer userId) throws Exception {
        taskDTO.setId(null);
        taskDTO.setStatus(0);
        taskDTO.setExecType(taskDTO.getExecType()==null?ExeType.DISTRIBUTE_TYPE.getValue(): taskDTO.getExecType());
        taskDTO.setParentId(taskDTO.getParentId());
        taskDTO.setUserId(userId);

        taskDTO.setExePlanTimes(1);
        if(taskDTO.getCycleType() == 0){
      //      taskDTO.setExePlanTimes(CronCountUtil.count(taskDTO.getExeStartTime(),taskDTO.getExeEndTime(),taskDTO.getCron()));
        }
        if(CollectionUtils.isNotEmpty(taskReq.getStartupTestList())){
            String json = JSONObject.toJSONString(taskReq.getStartupTestList());
            taskReq.getTaskDTO().setColdHotStartActivity(json);
            taskDTO.setColdHotStartActivity(json);
        }else{
            taskDTO.setColdHotStartActivity("{}");
        }

        if (!CollectionUtils.isEmpty(taskReq.getDuidList())) {
            if(taskDTO.getExecType().equals(ExeType.NON_DISTRIBUTE_TYPE.getValue())
                    && taskDTO.getCycleType() == 2){
                //非分布式
                taskDTO.setCreateTime(LocalDateTime.now());
                taskMapper.insert(taskDTO);
                Map<String, TaskDeviceInfo> map = taskReq.getDuidList().stream().collect(Collectors.toMap(TaskDeviceInfo::getDuid, v -> v,
                        (v1, v2) -> {
                            if(StringUtils.isNotEmpty(v1.getAuxDuid())) {
                                return v1;
                            }
                            return v2;
                        }));
                logger.info("duidList:{}, map:{}", JSONObject.toJSONString(taskReq.getDuidList()), JSONObject.toJSONString(map));
                for (String duid : map.keySet()) {
                    TaskDTO taskHist = new TaskDTO();
                    BeanUtils.copyProperties(taskDTO, taskHist);
                    this._newTask(taskHist, taskDTO.getId());
                    //准备任务设备数据
                    boolean rslt = taskManager.prepareTaskDevice(map.get(duid), taskHist);
                    if(!rslt){
                        updateStatusIfNotImmediateTask(taskDTO.getParentId());
                        return;
                    }
                    this._createJob(taskHist);
                }
            }else{
                this._newTask(taskDTO);
                //准备任务设备数据
                boolean rslt = taskManager.prepareTaskDeviceByTaskDeviceInfoList(taskReq.getDuidList(), taskDTO);
                if(!rslt){
                    updateStatusIfNotImmediateTask(taskDTO.getParentId());
                    return;
                }
                this._createJob(taskDTO);
            }
        }
    }

    private void _createJob(TaskDTO taskDTO) throws Exception {
        switch (taskDTO.getCycleType()) {
            case 0:
                this.createScheduleJob(taskDTO);
                break;
            case 1:
                if(StringUtils.isNotEmpty(taskDTO.getCron())) {
                    Long time = Long.valueOf(taskDTO.getCron());
                    Date date = new Date(System.currentTimeMillis() + time);
                    this.createScheduleJobOnceByTime(taskDTO.getId(), date);
                }else {
                    this.createScheduleJobOnceByTime(taskDTO.getId(), DateUtil.toDate(taskDTO.getExeStartTime()));
                }
                break;
            case 2:
                if(taskDTO.getType() != 3 && taskDTO.getType() != 4) {
                    String scriptIdsStr = taskDTO.getScriptIds();
                    String[] idsArr = scriptIdsStr.split(",");
                    if (idsArr.length == 0) {
                        throw new CustomException(ResultCode.DATA_IS_WRONG);
                    }
                    List<Integer> ids = Arrays.stream(idsArr)
                            .map(s -> Integer.parseInt(s.trim()))
                            .collect(Collectors.toList());
                    List<ScriptInfo2> scriptInfos = iScriptInfoServcie.selectScriptByScriptIds(ids);
                    List<String> scriptStringList = this.orderScripts(ids,scriptInfos);
                }
                //任务模块启动任务
                taskManager.startTask(taskDTO);
                break;
            default:
        }
    }

    private List<String> orderScripts(List<Integer> orderIds,List<ScriptInfo2> scriptInfos){
        if(CollectionUtils.isEmpty(orderIds) || CollectionUtils.isEmpty(scriptInfos)) throw new CustomException(ResultCode.DATA_IS_WRONG);
        List<String> orderList = new ArrayList<>();
        for (Integer id : orderIds){
            for (ScriptInfo2 scriptInfo : scriptInfos){
                if( id.intValue() ==  scriptInfo.getId().intValue()){
                    orderList.add(JSON.toJSONString(scriptInfo));
                    break;
                }
            }
        }
        return orderList;
    }
    @Override
//    @Transactional
    public ResponseResult resetTaskSchedule(TaskDTO taskDTO) throws Exception {
        this.checkNewTaskScheduleParam(taskDTO);
        // 校验名称是否重复
        if(!checkTaskNameExist(taskDTO.getProjectId(), taskDTO.getName(), taskDTO.getId())) {
            throw new CustomException(ResultCode.BUSINESS_TESTTASK_EXISTED);
        }
        // 校验项目与脚本是否匹配
        if(!checkProjectIdMatchScriptIdStrs(taskDTO.getProjectId(), taskDTO.getScriptIds())) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_SCRIPT_NOT_MATCH_ERR);
        }
        // 校验脚本是否同为性能测试脚本或者同为非性能测试脚本
        if(!checkScriptPerformanceTypeSame(taskDTO.getScriptIds())) {
            throw new CustomException(ResultCode.BUSINESS_SCRIPT_PERFORMANCE_TYPE_NOT_SAME);
        }
        AssertUtil.notNull(taskDTO.getId(), TASKID_NOTNULL);


        User currUser = (User) SecurityUtils.getSubject().getPrincipal();
        if (currUser == null) throw new CustomException(ResultCode.PERMISSION_EXPIRE);

        taskDTO.setExePlanTimes(1);
        if(taskDTO.getCycleType() == 0){
   //         taskDTO.setExePlanTimes(CronCountUtil.count(taskDTO.getExeStartTime(),taskDTO.getExeEndTime(),taskDTO.getCron()));
        }

        TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
        try {
            // 清除原有JOB
            TaskDTO task = taskMapper.getTaskById(taskDTO.getId());
            if (task == null) throw new CustomException(ResultCode.BUSINESS_TASK_NOT_FIND_ERR);
            List<TaskDTO> tasks = taskMapper.getTaskJobTriggerId(task.getId());

            this.taskMapper.removeTaskJob(taskDTO.getId());
            for (TaskDTO dto : tasks) {
                this.removeJob(dto.getId());
                this.taskMapper.removeTaskHistoryJob(dto.getId());
            }

            //修改任务计划信息
            taskDTO.setUserId(null); // 不允许修改创建人
            taskDTO.setCreateTime(null); //不允许修改创建时间
            this.taskMapper.update(taskDTO);

            // 创建新任务
            TaskDTO newTask = new TaskDTO();
            newTask.setProjectId(taskDTO.getProjectId());
            newTask.setName(taskDTO.getName());
            newTask.setType(taskDTO.getType());
            newTask.setCycleType(taskDTO.getCycleType());
            newTask.setCron(taskDTO.getCron());
            newTask.setScriptIds(taskDTO.getScriptIds());
            newTask.setPackages(taskDTO.getPackages());
            newTask.setDuid(taskDTO.getDuid());
            newTask.setExeNumber(taskDTO.getExeNumber());
            newTask.setMonkeyExeNumber(taskDTO.getMonkeyExeNumber());
            newTask.setStatus(0);
            newTask.setExecType(0);
            newTask.setStartJob(0);
            newTask.setParentId(task.getId());
            newTask.setUserId(task.getUserId());
            newTask.setExePlanTimes(task.getExePlanTimes());
            newTask.setExeStartTime(task.getExeStartTime());
            newTask.setExeEndTime(task.getExeEndTime());
            newTask.setCreateTime(LocalDateTime.now());
            this.taskMapper.insertHistory(newTask);

            // 创建新执行JOB
            try {
                this._createJob(newTask);
            } catch (Exception e) {
                // 还原计划任务
                TaskDTO _taskDto;
                for (TaskDTO dto : tasks) {
                    _taskDto = this.taskMapper.getTaskHistoryById(dto.getId());
                    this._createJob(_taskDto);
                }
                throw e;
            }
            transactionManager.commit(status);
            return ResponseResult.defaultSuccess(newTask.getId());
        } catch (Exception e) {
            logger.error(e.getMessage());
            transactionManager.rollback(status);
            throw new CustomException(ResultCode.SYSTEM_INNER_ERROR);
        }
    }

    @Override
    public int newAutoTaskHistory(TaskDTO taskDTO) {
        AssertUtil.notNull(taskDTO, "参数异常");
        taskDTO.setCreateTime(LocalDateTime.now());
        return taskMapper.insertHistory(taskDTO);
    }

    @Override
    @Transactional
    public int taskFailById(Integer historyTaskid, String failReason) {
        if (historyTaskid == null) return 0;
        int count = 0;
        TaskDTO taskById = taskMapper.getTaskHistoryById(historyTaskid);
        count = taskMapper.taskFailById(taskById.getParentId(), failReason);
        count += taskMapper.taskHistoryFailById(historyTaskid, failReason);

        this.iDeviceServcie.clearDeviceTaskInfo(taskById.getDuid(),taskById.getId(),taskById.getUserId());
        screenDataService.taskChangedStatus(WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXEFAILED,taskById.getProjectId(),historyTaskid);

        return count;
    }

    @Override
    @Transactional
    public int interrupterWhenExecing(String duid) {
        if (StringUtils.isEmpty(duid)) return 0;
        int count = 0;
        count = taskMapper.interrupterWhenExecingHistory(duid);
        count += taskMapper.interrupterWhenExecing(duid);

        List<TaskDTO> taskHistoryByInterrupterWhenExecing = taskMapper.getTaskHistoryByInterrupterWhenExecing(duid);
        if(!CollectionUtils.isEmpty(taskHistoryByInterrupterWhenExecing)){
            for (TaskDTO task:taskHistoryByInterrupterWhenExecing) {
                screenDataService.taskChangedStatus(WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXEFAILED,task.getProjectId(),task.getId());
            }
        }
        return count;
    }

    @Override
    @Transactional
    public ResponseResult execTask(TaskExecDTO taskExecDTO) {
        AssertUtil.notNull(taskExecDTO, "参数异常");
        AssertUtil.notNull(taskExecDTO.getId(), TASKID_NOTNULL);
        AssertUtil.notNull(taskExecDTO.getStatus(), "任务状态不允许为空");
        AssertUtil.notNull(taskExecDTO.getExecTime(), "任务执行时间不允许为空");

        Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(taskExecDTO.getId()));
        if (subtask == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
        if(subtask.getTaskStatus().equals(SubTaskStatus.SEND.getValue())){
            feedbackManager.feedbackForTaskStart(subtask);
            TaskDTO taskHistoryById = taskMapper.getTaskHistoryById(subtask.getTaskId().intValue());
            if (taskHistoryById == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
            screenDataService.taskChanged(taskHistoryById.getProjectId());
        }else{
            logger.error("execTask--ubtask status={}",subtask.getTaskStatus());
        }
        return ResponseResult.defaultSuccess(null);
    }

    @Override
    public void taskChangedAfterStartTask(TaskExecDTO taskExecDTO){
        AssertUtil.notNull(taskExecDTO, "参数异常");
        AssertUtil.notNull(taskExecDTO.getId(), TASKID_NOTNULL);
        Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(taskExecDTO.getId()));
        if (subtask == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
        TaskDTO taskHistoryById = taskMapper.getTaskHistoryById(subtask.getTaskId().intValue());
        if (taskHistoryById == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
        screenDataService.taskChanged(taskHistoryById.getProjectId());
    }

    private static ConcurrentHashMap<Integer, ReentrantLock> lockMap = new ConcurrentHashMap<>();

    private ReentrantLock getLock(Integer taskId){
        ReentrantLock lock = lockMap.get(taskId);
        if(lock == null){
            lock = new ReentrantLock();
            lockMap.put(taskId, lock);
        }
        return lock;
    }

    @Override
    @Transactional
    public ResponseResult taskComplete(TaskDataDTO taskDataDTO) {
        AssertUtil.notNull(taskDataDTO, "参数异常");
        AssertUtil.notNull(taskDataDTO.getTask(), "参数异常");
        AssertUtil.notNull(taskDataDTO.getTask().getId(), TASKID_NOTNULL);
        AssertUtil.notNull(taskDataDTO.getTask().getStatus(), "任务状态不允许为空");
        String duid = taskDataDTO.getTask().getDuid();
        AssertUtil.hasText(duid, "设备ID不允许为空");

        Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(taskDataDTO.getTask().getId()));
        if (subtask == null){
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }
        if (subtask.getTaskStatus().equals(SubTaskStatus.SUCCESS.getValue())
                ||subtask.getTaskStatus().equals(SubTaskStatus.FAIL.getValue())
                ||subtask.getTaskStatus().equals(SubTaskStatus.FAIL_CANCEL_SEND.getValue())) {
            logger.info("taskComplete 重复提交--------------taskId: {}", taskDataDTO.getTask().getId());
            return ResponseResult.defaultSuccess(null);
        }
        //按taskId获取锁
        ReentrantLock lock = getLock(taskDataDTO.getTask().getId());
        try {
            try {
                lock.tryLock(1, TimeUnit.SECONDS);
            } catch (InterruptedException e) {
                logger.info("tryLock error");
                return ResponseResult.defaultSuccess(null);
            }
            TaskDTO taskHistory = taskMapper.getTaskHistoryById(subtask.getTaskId().intValue());
            if(StringUtils.isNotEmpty(taskDataDTO.getTask().getFailReason())){
                if("设备正在执行任务".equals(taskDataDTO.getTask().getFailReason())){
                    subtask.setTaskStatus(SubTaskStatus.CREATE.getValue());
                    subtask.setFailReason("device busy retry");
                    subtaskMapper.updateByPrimaryKey(subtask);
                    feedbackManager.feedbackForDeviceClear(subtask);
                    return ResponseResult.defaultSuccess(null);
                }
            }
            if(taskHistory.getType()==4){
                taskHistory.setSuccessNumber(taskHistory.getExeNumber());
                taskHistory.setFailNumber(-1);
                //taskDataDTO.getTask().setSuccessNumber(taskHistory.getExeNumber());
            }
            //处理子任务
            boolean rslt = handleSubtask(taskDataDTO, subtask, taskDataDTO.getReportList());
            if (!rslt) {
                //反馈设备事件
                logger.info("taskComplete handleSubtask---false----taskId: {}", taskDataDTO.getTask().getId());
                feedbackManager.feedbackForDeviceFree(subtask);
                return ResponseResult.defaultSuccess(null);
            }
            //更新taskHistory,task
            handleTask(taskHistory, taskDataDTO.getStartupLogList());

            //保存执行结果
            ProjectProductionConfigExample example = new ProjectProductionConfigExample();
            example.createCriteria().andProjectIdEqualTo(taskHistory.getProjectId());
            List<ProjectProductionConfig> projectProductionConfigs = projectProductionConfigMapper.selectByExample(example);
            logger.info("projectProductionConfigs {}",projectProductionConfigs);
            ProjectProductionConfig projectProductionConfig = null;
            if(CollectionUtils.isNotEmpty(projectProductionConfigs)) {
                projectProductionConfig = projectProductionConfigs.get(0);
            }
            ProjectInfo projectInfo = projectMapper.selectByPrimaryKey(taskHistory.getProjectId());
            if (!CollectionUtils.isEmpty(taskDataDTO.getReportList())) {
                List<ScriptInfo2> scriptInfos =Lists.newArrayList();
                if(taskHistory.getType().intValue() != 3 && StringUtils.isNotEmpty(taskHistory.getScriptIds())) {
                    scriptInfos = scriptInfoMapper.selectScriptByScriptIdsWithoutStatus(
                            Arrays.asList(taskHistory.getScriptIds().split(",")).stream().map(x -> Integer.parseInt(x)).collect(Collectors.toList()));
                }
                for (TestReportDTO dto : taskDataDTO.getReportList()) {
                    String monkeyReport2 = dto.getMonkeyReport2();
                    logger.info("monkeyReport2 {}", monkeyReport2);
                    TestReport testReport =new TestReport();
                    BeanUtils.copyProperties(dto, testReport);
                    testReport.setCanLogFileId(dto.getCanLogFileId());
                    testReport.setHtmlReportFileId(dto.getHtmlReportFileId());
                    testReport.setSubtaskId(Long.valueOf(dto.getTaskId()));
                    testReport.setTaskId(subtask.getTaskId().intValue());

                    if(taskHistory.getType().intValue() == 3 && null != projectProductionConfig && StringUtils.isNotEmpty(projectProductionConfig.getBugUrl())){
//                        String title = String.format("[%s]", projectInfo.getProjectName());
//                        if(CollectionUtils.isNotEmpty(scriptInfos)) {
//                            title += String.format(" [%s]", scriptInfos.get(0).getProModuleName());
//                            title += String.format(" [%s]", scriptInfos.get(0).getName());
//                        }
                        String monkeyReport = testReport.getMonkeyReport2();
                        logger.info("monkeyReport {}", monkeyReport);
                        TestReportJiraDTO testReportJiraDTO = JSONUtil.toBean(monkeyReport, TestReportJiraDTO.class);
                        logger.info("monkey report list {}", testReportJiraDTO.getMonkeyExceptionInfos());
                        List<TestReportJiraEntity> testReportJiraEntities = testReportJiraDTO.getMonkeyExceptionInfos();


                        if(CollectionUtils.isNotEmpty(testReportJiraEntities)){
                            Map<String, Map<String, List<TestReportJiraEntity>>> map = testReportJiraEntities.stream().collect(Collectors.groupingBy(TestReportJiraEntity::getPackageName, Collectors.groupingBy(TestReportJiraEntity::getExceptionType, Collectors.toList())));

                            List<TestReportJiraVO> list = new ArrayList<>();
                            map.entrySet().stream().forEach(entry -> {
                                TestReportJiraVO testReportJiraVO = new TestReportJiraVO();
                                testReportJiraVO.setPackagesName(entry.getKey());
                                testReportJiraVO.setCpu("0");
                                testReportJiraVO.setMemory("0");
                                Map<String, List<TestReportJiraEntity>> value = entry.getValue();
                                logger.info("key {} value {}",entry.getKey(), entry.getValue());
                                if(CollectionUtils.isNotEmpty(value.keySet())){
                                    List<TestReportJiraEntity> totalCrashList = new ArrayList<>();
                                    List<TestReportJiraEntity> crashList = value.get("Crash");
                                    List<TestReportJiraEntity> nativeCrashList = value.get("Native_Crash");
                                    List<TestReportJiraEntity> oomCrashList = value.get("OOM");
                                    if(CollectionUtils.isNotEmpty(crashList)) {
                                        totalCrashList.addAll(crashList);
                                    }
                                    if(CollectionUtils.isNotEmpty(nativeCrashList)) {
                                        totalCrashList.addAll(nativeCrashList);
                                    }
                                    if(CollectionUtils.isNotEmpty(oomCrashList)) {
                                        totalCrashList.addAll(oomCrashList);
                                    }
                                    testReportJiraVO.setCrashCount(totalCrashList.size());
                                    List<TestReportJiraEntity> anrList = new ArrayList<>();
                                    anrList = value.get("ANR");
                                    if(CollectionUtils.isNotEmpty(anrList)) {
                                        testReportJiraVO.setAnrCount(anrList.size());
                                    }
                                    list.add(testReportJiraVO);
                                }
                            });

                            String s = JSONUtil.toJsonStr(list);
                            logger.info("monkey report  {}", s);
                            testReport.setMonkeyReport(s);
                            for (int i = 0; i < testReportJiraEntities.size(); i++) {

                                TestReportJiraEntity testReportJiraEntity = testReportJiraEntities.get(i);

//                                if () {
                                    logger.info("monkey report need to create bug {}", testReportJiraEntity.toString());
                                    DeviceInfo deviceInfo = deviceInfoMapper.selectByDuid(duid);
                                    String vin = deviceInfo.getVin();
                                    String now = DateUtil.format(new Date());
                                    String title = String.format("%s %s %s %s %s %s %s", "[T12A]", "[台架]", "[A3]", "[Monkey]",
                                            String.format("[%s]", vin), String.format("[%s]", now), String.format("[%s]", testReportJiraEntity.getPackageName()));
                                    String stepContent = testReportJiraEntity.getException();
                                    logger.info("handler bug content %s {}", stepContent);
                                    IBugHandler handler = AbstractBugHandler.getBugHandler(projectProductionConfig.getBugSubmitTo());
                                    logger.info("handler bug EnableJira() %s {}", taskHistory.getEnableJira());
                                    if (null != handler && taskHistory.getEnableJira() == 1) {
                                        String[] bugInfo = handler.handleBugForMonkey(title, stepContent, testReportJiraEntity, testReportJiraDTO, projectProductionConfig);
                                        if (null != bugInfo && bugInfo.length == 2) {
                                            testReport.setBugId(bugInfo[0]);
                                            testReport.setBugUrl(bugInfo[1]);
                                        }
                                    }
//                                }
                            }
                        }

                    }
                    // 流水线自动化测试需要将bug登录到缺陷系统中
                    if((taskHistory.getType().intValue() == 2 ) && testReport.getStatus().intValue() == 0
                            && null != projectProductionConfig && StringUtils.isNotEmpty(projectProductionConfig.getBugUrl())) {
                        String title = String.format("[%s]", projectInfo.getProjectName());
                        if(CollectionUtils.isNotEmpty(scriptInfos)) {
                            title += String.format(" [%s]", scriptInfos.get(0).getProModuleName());
                            title += String.format(" [%s]", scriptInfos.get(0).getName());
                        }
                        title += " " + testReport.getContent();
                        String stepContent = getStepContent(scriptInfos, testReport.getScriptId());
                        logger.info("handler bug content %s {}",stepContent);
                        IBugHandler handler = AbstractBugHandler.getBugHandler(projectProductionConfig.getBugSubmitTo());
                        logger.info("handler bug EnableJira() %s {}",taskHistory.getEnableJira());
                        if(null != handler && taskHistory.getEnableJira() == 1) {
                            String[] bugInfo = handler.handleBug(title, stepContent, testReport, projectProductionConfig);
                            if(null != bugInfo && bugInfo.length == 2) {
                                testReport.setBugId(bugInfo[0]);
                                testReport.setBugUrl(bugInfo[1]);
                            }
                        }
                    }
                    iTestReportService.insert(testReport);
                    if(!CollectionUtils.isEmpty(dto.getReportErrorVideoInfos())) {
                        for (TestReportErrorVideoInfo testReportErrorVideoInfo : dto.getReportErrorVideoInfos()) {
                            testReportErrorVideoInfo.setTestReportId(testReport.getId());
                            testReportErrorVideoInfo.setCreateTime(new Date());
                            testReportErrorVideoInfoMapper.insert(testReportErrorVideoInfo);
                        }
                    }
                    if(!CollectionUtils.isEmpty(dto.getTestedReport())) {
                        handleTestedReport(testReport.getId(), dto);
                    }
                }
            }
            // 解绑设备
            iDeviceServcie.clearDeviceTaskInfo(subtask.getDuid(), subtask.getTaskId().intValue(), taskHistory.getUserId());
            //反馈设备事件
            feedbackManager.feedbackForDeviceFree(subtask);
            //在任务未完成时候，部分完成时候触发刷新大屏，而当任务结束会由其他地方刷新taskList
            if(taskHistory.getStatus() == TaskStatus.TASK_EXEC.getValue()) {
                //大屏数据刷新
                screenDataService.refreshTaskInfo(taskHistory.getUserId().toString(), taskHistory.getProjectId());
            }
            screenDataService.taskChangedStatus(taskDataDTO.getTask().getStatus().equals(TaskStatus.TASK_COMPLETE.getValue()) ? WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXECOMPLETE : WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXEFAILED
                    , taskHistory.getProjectId(), taskHistory.getId());
        }finally {
            lock.unlock();
        }
        return ResponseResult.defaultSuccess(null);
    }

    private boolean handleSubtask(TaskDataDTO taskDataDTO, Subtask subtask, List<TestReportDTO> reportList) {
        TaskExecDTO taskExecDTO = taskDataDTO.getTask();
        if(subtask.getRetryOriginId()>0) {
            //如果当前substack是重试，并且原来那个的成功了，当前应为取消
            Subtask subtaskOri = subtaskMapper.selectByPrimaryKey(subtask.getRetryOriginId());
            if(subtaskOri.getTaskStatus().equals(SubTaskStatus.SUCCESS.getValue())){
                subtask.setTaskStatus(SubTaskStatus.FAIL.getValue());
                subtaskMapper.updateByPrimaryKey(subtask);
                return false;
            }
        }

        //prepare subtask
        int successSum = Integer.parseInt(String.valueOf(reportList.stream().filter(report -> report.getStatus().intValue() == 1).count()));
        int failSum  = Integer.parseInt(String.valueOf(reportList.stream().filter(report -> report.getStatus().intValue() == 0).count()));
        if(successSum + failSum < subtask.getNeedTimes()){
            int noExeNum = subtask.getNeedTimes() - successSum - failSum;
            failSum = failSum + noExeNum;
        }
        subtask.setSuccessTimes(successSum);
        subtask.setFailReason(taskExecDTO.getFailReason());
        subtask.setFailTimes(failSum);
        if(TaskStatus.TASK_COMPLETE.getValue() == taskExecDTO.getStatus()) {
            subtask.setTaskStatus(SubTaskStatus.SUCCESS.getValue());
        }else if(TaskStatus.TASK_FAIL.getValue() == taskExecDTO.getStatus()) {
            //失败也算完成，算子任务成功
            subtask.setTaskStatus(SubTaskStatus.SUCCESS.getValue());
        }else{
            subtask.setTaskStatus(SubTaskStatus.FAIL.getValue());
        }
        subtaskMapper.updateByPrimaryKey(subtask);
        logger.info("taskComplete handleSubtask---over----subtaskId: {},subtaskstatus:{}", subtask.getId(),subtask.getTaskStatus());
        //feedbackManager.feedbackForSubtaskComplete(subtask);
        return  true;
    }


    private void handleTask(TaskDTO taskHistory, List<ColdHotStartupReportDTO> startupLogList){
        int scriptsLen = 1;
        if(StringUtils.isNotEmpty(taskHistory.getScriptIds())) {
            scriptsLen = taskHistory.getScriptIds().split(",").length;
        }
        TaskExecDTO te = new TaskExecDTO();
        te.setId(taskHistory.getId());
        //状态暂为处理中
        te.setStatus(TaskStatus.TASK_EXEC.getValue());
        te.setExecTime(null);
        SubtaskExample subtaskExample = new SubtaskExample();
        subtaskExample.createCriteria().andTaskIdEqualTo(Long.valueOf(taskHistory.getId())).andTaskStatusEqualTo(SubTaskStatus.SUCCESS.getValue());
        List<Subtask> subtasks = subtaskMapper.selectByExample(subtaskExample);
        if(CollectionUtils.isEmpty(subtasks)){
            return ;
        }
        int failSum  = 0;
        for(Subtask st:subtasks){
            failSum = failSum+ st.getFailTimes();
        }

        te.setFailNumber(getNumForDivide(failSum, scriptsLen));
        te.setSuccessNumber(taskHistory.getExeNumber() - te.getFailNumber());

        //执行次数都完成
        int exeTime =getNumForDivide(taskHistory.getExeNumber() * (taskHistory.getScriptIds().split(",").length), DistributeManager.SUBTASK_LEN);
//        if(taskHistory.getType() == 4){
//            taskHistory.setSuccessNumber(taskHistory.getExeNumber());
//            taskHistory.setFailNumber(0);
//            te.setSuccessNumber(taskHistory.getExeNumber());
//            te.setFailNumber(0);
//        }
        if(subtasks.size() >= exeTime) {
            //会大于是因为取余数进一位，所以有可能累加次数大于1
            te.setStatus(TaskStatus.TASK_COMPLETE.getValue());
            //反馈发送任务完成事件
            feedbackManager.feedbackForTaskComplete(taskHistory);
        }
        taskMapper.execTaskHistory(te);
        te.setId(taskHistory.getParentId());
        TaskDTO task = taskMapper.getTaskById(taskHistory.getParentId());
        te.setExceEndTime(LocalDateTime.now());
        if(task!=null && task.getExecType().equals(ExeType.NON_DISTRIBUTE_TYPE.getValue())) {
            //非分布式
            TaskSearchDTO tsDTO = new TaskSearchDTO();
            tsDTO.setParentId(taskHistory.getParentId());
            List<TaskDTO> listByParentid = taskMapper.getTaskHistListByParentid(tsDTO);
            if(CollectionUtils.isEmpty(listByParentid)){
                return;
            }
            for(TaskDTO th:listByParentid){
                if(!th.getStatus().equals(TaskStatus.TASK_COMPLETE.getValue())
                        && !th.getStatus().equals(TaskStatus.TASK_NO_DEVICE_FAIL.getValue())
                        && !th.getStatus().equals(TaskStatus.TASK_CANCEL.getValue())){
                    return;
                }
            }
            // 冷热启动
            if(taskHistory.getFailNumber() == -1){
                te.setSuccessNumber(taskHistory.getExeNumber());
                te.setFailNumber(0);
            }
            //全部taskHistory都结束
            taskMapper.execTask(te);
        }else{
            taskMapper.execTask(te);
        }
        logger.info("check startupLogList:");
        if(task!=null && CollectionUtils.isNotEmpty(startupLogList)){
            logger.info("startupLogList:{}",JSONObject.toJSONString(startupLogList));
            int count = 0;
            for(ColdHotStartupReportDTO chsDto : startupLogList){
                if(CollectionUtils.isNotEmpty(chsDto.getStartData())){
                    for(Integer time : chsDto.getStartData()){
                        TaskColdHotStartupLog log = new TaskColdHotStartupLog();
                        log.setActivityName(chsDto.getActivityName());
                        log.setPackageName(chsDto.getPackageName());
                        log.setType(chsDto.getType());
                        log.setStartupResponseTime(time);
                        log.setTaskId(task.getId());
                        log.setCreateTime(new Date());
                        taskColdHotStartupLogMapper.insertSelective(log);
                    }
                    count = count+chsDto.getTestTimes();
                }
            }
            /*te.setSuccessNumber(count);
            te.setFailNumber(0);
            taskHistory.setFailNumber(0);
            taskHistory.setSuccessNumber(count);
            te.setId(task.getId());
            taskMapper.execTask(te);*/
        }
    }

    /**
     * 余数进1位
     * @param a
     * @param b
     * @return
     */
    private int getNumForDivide(int a, int b){
        if(b==0){
            return 0;
        }
        return (a/b) + (a%b!=0?1:0);
    }

    @Override
    @Transactional
    public ResponseResult taskProcess(TaskProcessDTO taskProcessDTO) {
        AssertUtil.notNull(taskProcessDTO, "参数异常");
        AssertUtil.notNull(taskProcessDTO.getId(), TASKID_NOTNULL);
        AssertUtil.notNull(taskProcessDTO.getTimes(), "执行次数不允许为空");

        Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(taskProcessDTO.getId()));

        TaskDTO taskHistoryById = taskMapper.getTaskHistoryById(subtask.getTaskId().intValue());
        if (taskHistoryById == null) throw new CustomException(ResultCode.DATA_IS_WRONG);

        taskMapper.execTaskProcessTimesHistory(taskProcessDTO.getId(),taskProcessDTO.getTimes());
        taskMapper.execTaskProcessTimes(taskHistoryById.getParentId(),taskProcessDTO.getTimes());

        screenDataService.taskChangedStatus(WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXEPROCESS,taskHistoryById.getProjectId()
                ,taskHistoryById.getId() +"#"+ formatPercent(taskProcessDTO.getTimes(),taskHistoryById.getExeNumber()));
        return ResponseResult.defaultSuccess(null);
    }

    @Override
    @Transactional
    public int triggerTaskJob(Integer taskId) {
        AssertUtil.notNull(taskId, TASKID_NOTNULL);

        TaskDTO taskHistoryById = taskMapper.getTaskHistoryById(taskId);
        if (taskHistoryById == null) throw new CustomException(ResultCode.DATA_IS_WRONG);

        int count = 0;
        count = taskMapper.triggerTaskHistoryJob(taskId);
        count += taskMapper.triggerTaskJob(taskHistoryById.getParentId());
        return count;
    }

    @Override
    public void modifyTaskJob(Integer oldTaskId, TaskDTO newTaskDTO) throws Exception {
        AssertUtil.notNull(oldTaskId, "旧任务ID不允许为空");
        AssertUtil.notNull(newTaskDTO, "新任务不允许为空");
        AssertUtil.notNull(newTaskDTO.getId(), "新任务ID不允许为空");
        AssertUtil.notNull(newTaskDTO.getCron(), "新任务时间不允许为空");

        this.removeJob(oldTaskId);
        this.createScheduleJob(newTaskDTO);
    }

    @Override
    public TaskDTO getLastTaskHistoryByTaskId(Integer taskId) {
        AssertUtil.notNull(taskId, TASKID_NOTNULL);
        return taskMapper.getLastTaskHistoryByTaskId(taskId);
    }

    @Override
    public TaskDTO getTaskHistoryById2(Integer taskId) {
        AssertUtil.notNull(taskId, TASKID_NOTNULL);
        return taskMapper.getTaskHistoryById2(taskId);
    }

    @Override
    public Map<String, Object> getTaskUserInfoById(Integer taskId) {
        AssertUtil.notNull(taskId, TASKID_NOTNULL);
        TaskDTO taskDTO = taskMapper.getTaskUserInfoById(taskId);
        if(taskDTO == null){
            Subtask subtask = subtaskMapper.selectByPrimaryKey(Long.valueOf(taskId));
            if(subtask == null){
                if(taskDTO == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
            }
            taskDTO = taskMapper.getTaskUserInfoById(subtask.getTaskId().intValue());
        }
        if(taskDTO == null) throw new CustomException(ResultCode.DATA_IS_WRONG);
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("taskId",taskDTO.getId());
        userInfo.put("userId",taskDTO.getUserId());
        userInfo.put("userName",taskDTO.getUserName());
        return userInfo;
    }

    @Override
    @Transactional
    public int taskExecing(Integer historyTaskId) {
        logger.info("TaskService taskExecing.......任务重启");
        AssertUtil.notNull(historyTaskId, TASKID_NOTNULL);
        TaskDTO taskHistoryById = taskMapper.getTaskHistoryById(historyTaskId);

        AssertUtil.notNull(taskHistoryById, "查无任务");
        AssertUtil.notNull(taskHistoryById.getParentId(), "任务数据异常");

        taskMapper.taskExec(taskHistoryById.getParentId());
        int i = taskMapper.taskHistoryExec(historyTaskId);

        screenDataService.taskChanged(taskHistoryById.getProjectId());
        return i;

    }


    @Override
    @Transactional
    public ResponseResult cancelTask(Integer id) {
        AssertUtil.notNull(id, TASKID_NOTNULL);
        TaskDTO task = taskMapper.getTaskById(id);
        if (task == null) throw new CustomException(ResultCode.BUSINESS_TASK_NOT_FIND_ERR);

        if ((task.getType() != 1 && task.getType() != 2 && task.getType() != 3 && task.getType() != 4) || (task.getStatus() != 0 && task.getStatus() != 1)) throw new CustomException(ResultCode.BUSINESS_TASK_REMOVEJOB_ERR);

        List<TaskDTO> tasks = taskMapper.getTaskJobTriggerId(task.getId());
        if (tasks == null || CollectionUtils.isEmpty(tasks)) throw new CustomException(ResultCode.DATA_IS_WRONG);

        taskMapper.cancelTask(task.getId());
        for (TaskDTO dto : tasks) {
            taskMapper.cancelTaskHistory(dto.getId());
            if (dto.getCycleType() != 2) {
                this.removeJob(dto.getId());
            }
            //发取消事件---
            feedbackManager.feedbackForTaskCancel(dto);
        }

        // 2. 解绑设备
        if(StringUtils.isNotEmpty(task.getDuid())) {
            iDeviceServcie.clearDeviceTaskInfo2(task.getDuid(), id, task.getUserId());
        }
        // 3. （通知上位机）正在执行的任务通知上位机取消 (在状态修改之前查询 包括等待执行、正在执行设备占用状态，更新消息之后再通知上位机)
//        if (!map.isEmpty()) {
//            WSScriptMessage message = new WSScriptMessage();
//            for (Map.Entry<String, List<Integer>> entry : map.entrySet()) {
//                try {
//                    message.setTouuid(entry.getKey());
//                    message.setFromuuid("server");
//                    message.setMsgcode("204");  // 取消正在执行的任务
//                    message.setTaskIds(entry.getValue());
//                    iDeviceServcie.sendToDeviceMessage(entry.getKey(), message.toString());
//                    screenDataService.taskChangedStatus(WebSocketServerScreen.SCREEN_CODE_DATA_TASKEXEFAILED,task.getProjectId(),entry.getValue());
//                } catch (Exception e) {
//                    e.printStackTrace();
//                }
//            }
//        }

        return ResponseResult.defaultSuccess(null);
    }

    public ResponseResult removeTask(Integer id){
        AssertUtil.notNull(id, TASKID_NOTNULL);
        TaskDTO task = taskMapper.getTaskById(id);
        if (task == null) throw new CustomException(ResultCode.BUSINESS_TASK_NOT_FIND_ERR);
        if(task.getStatus().equals(TaskStatus.TASK_FAIL.getValue())
                || task.getStatus().equals(TaskStatus.TASK_CANCEL.getValue())
                ||task.getStatus().equals(TaskStatus.TASK_NO_DEVICE_FAIL.getValue())){
            task.setIsDeleted(1);
            taskMapper.update(task);

            TaskSearchDTO taskSearchDTO = new TaskSearchDTO();
            taskSearchDTO.setParentId(task.getId());
            List<TaskDTO> listByParentid = taskMapper.getTaskHistListByParentid(taskSearchDTO);
            if(!CollectionUtils.isEmpty(listByParentid)){
                for(TaskDTO taskDTO : listByParentid ){
                    taskDTO.setIsDeleted(1);
                    taskMapper.updateHistory(taskDTO);
                }
            }
            return ResponseResult.defaultSuccess(null);
        }else{
            throw new CustomException(ResultCode.BUSINESS_TASK_REMOVETASK_ERR);
        }
        //return ResponseResult.defaultSuccess(null);
    }

    @Override
    public boolean removeTaskNotFoundJob(Integer taskId) {
        try {
            return this.removeJob(taskId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    private void createScheduleJob(TaskDTO taskDTO) throws Exception {
        if (taskDTO.getId() == null) throw new CustomException(ResultCode.PARAM_NOT_COMPLETE);
        String _taskId = String.valueOf(taskDTO.getId());
        // 启动调度器
        scheduler.start();
        //构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(JOB_CLASS_NAME, _taskId).getClass()).withIdentity(JOB_CLASS_NAME, _taskId).build();
        //表达式调度构建器(即任务执行的时间)
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(taskDTO.getCron());
        //按新的cronExpression表达式构建一个新的trigger
        CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_CLASS_NAME, _taskId).withSchedule(scheduleBuilder).build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            logger.error("createScheduleJob 创建定时任务失败:{}" , e);
            throw new Exception("创建定时任务失败");
        }
    }

    private void createScheduleJobOnceByTime(Integer taskId, Date startTime) throws Exception {
        if (taskId == null) throw new CustomException(ResultCode.PARAM_NOT_COMPLETE);
        String _taskId = String.valueOf(taskId);
        // 启动调度器
        scheduler.start();
        //构建job信息
        JobDetail jobDetail = JobBuilder.newJob(getClass(JOB_CLASS_NAME, _taskId).getClass()).withIdentity(JOB_CLASS_NAME, _taskId).build();
        //按新的cronExpression表达式构建一个新的trigger
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity(JOB_CLASS_NAME, _taskId).startAt(startTime).build();

        try {
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (SchedulerException e) {
            logger.error("createScheduleJobOnceByTime 创建定时任务失败:{}" , e);
            throw new Exception("创建定时任务失败");
        }
    }

    /**
     * @Description: 移除一个任务
     */
    private boolean removeJob(Integer taskId) {
        logger.info("removeJob: {}", taskId);
        try {
            if (taskId == null) throw new CustomException(ResultCode.PARAM_NOT_COMPLETE);
            String jobName = JOB_CLASS_NAME;
            String jobGroupName = taskId.toString();
            String triggerName = JOB_CLASS_NAME;
            String triggerGroupName = taskId.toString();

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            scheduler.pauseTrigger(triggerKey);// 停止触发器
            scheduler.unscheduleJob(triggerKey);// 移除触发器
            return scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private BaseJob getClass(String classname, String taskId) throws Exception {
//        Class<?> class1 = Class.forName(classname);
//        BaseJob classResult = (BaseJob) class1.newInstance();
//        return classResult;
        return autoTestJobV2;
    }

    private Integer _newTask(TaskDTO taskDTO) {
        taskDTO.setCreateTime(LocalDateTime.now());
        taskMapper.insert(taskDTO);
        taskDTO.setParentId(taskDTO.getId());
        taskDTO.setId(null);
        taskDTO.setCreateTime(LocalDateTime.now());
        taskMapper.insertHistory(taskDTO);
        Integer newTaskId = taskDTO.getId();
        if (newTaskId <= 0) {
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }
        return newTaskId;
    }

    private Integer _newTask(TaskDTO taskDTO, Integer parentId) {
        taskDTO.setParentId(parentId);
        taskDTO.setId(null);
        taskDTO.setCreateTime(LocalDateTime.now());
        taskMapper.insertHistory(taskDTO);
        Integer newTaskId = taskDTO.getId();
        if (newTaskId <= 0) {
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }
        return newTaskId;
    }

    private String formatPercent(Integer a ,Integer b){
        DecimalFormat df = new DecimalFormat("0.00");
        try {
            return df.format((double) a / b * 100) +"%";
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 项目与关联脚本的校验
     * 
     * @param projectId
     * @param scriptIdStrs
     * @return
     */
    private boolean checkProjectIdMatchScriptIdStrs(Integer projectId, String scriptIdStrs) {

        if(StringUtils.isEmpty(scriptIdStrs) || null == projectId) {
            return false;
        }
        List<Integer> scriptIds = Lists.newArrayList(scriptIdStrs.split(",")).stream().filter(scriptIdStr -> {
            if(StringUtils.isEmpty(scriptIdStr)) {
                return false;
            }
            return StringUtils.isNotEmpty(scriptIdStr.trim());
        }).map(scriptIdStr -> Integer.parseInt(scriptIdStr.trim())).distinct().collect(Collectors.toList());
        List<ScriptInfo2> scriptInfoList = iScriptInfoServcie.selectScriptByScriptIds(scriptIds);
        if(null == scriptInfoList || scriptInfoList.size() != scriptIds.size()) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_SCRIPT_NOT_EXISTED_RESELECT);
        }
        // 获取模块信息
        List<ProjectModuleDTO> proModules = projectModuleMapper.select(new ProjectModuleSearch() {{setProjectId(projectId);}});
        if(CollectionUtils.isEmpty(proModules)) {
            return false;
        }
        return scriptInfoList.stream().allMatch(scriptInfo -> {
            return proModules.stream().anyMatch(module -> scriptInfo.getProjectModuleId().intValue() == module.getId().intValue());
        });
    }

    /**
     * 校验测试任务名称是否存在
     * 
     * @param projectId
     * @param taskName
     * @param taskId
     * @return
     */
    private boolean checkTaskNameExist(Integer projectId, String taskName, Integer taskId) {
        List<TaskDTO> taskDTOs = taskMapper.getTaskList(new TaskSearchDTO() {{setProjectId(projectId); setName(taskName);}});
        if(CollectionUtils.isEmpty(taskDTOs)) {
            return true;
        }
        taskDTOs = taskDTOs.stream().filter(taskDTO -> StringUtils.equals(taskName, taskDTO.getName())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(taskDTOs)) {
            return true;
        }
        if(taskId == null) {
            return false;
        }
        return taskDTOs.stream().anyMatch(taskDTO -> taskId.intValue() == taskDTO.getId().intValue());
    }

    /**
     * 校验脚本是否同为性能测试脚本或者同为非性能测试脚本
     * 
     * @param scriptIds
     * @return
     */
    private boolean checkScriptPerformanceTypeSame(String scriptIds) {
        if(StringUtils.isEmpty(scriptIds)) {
            return false;
        }
        List<ScriptInfo2> scriptInfos = iScriptInfoServcie.selectScriptByScriptIds(Arrays.stream(scriptIds.split(","))
                .map(s -> Integer.parseInt(s.trim()))
                .collect(Collectors.toList()));
        if(CollectionUtils.isEmpty(scriptInfos)) {
            return false;
        }
        return scriptInfos.stream().allMatch(x -> x.getPerformanceType().shortValue() == scriptInfos.get(0).getPerformanceType().shortValue());
    }

    public static void main(String[] args) {
        System.out.println(System.currentTimeMillis());
    }

    /**
     * 检测有无在线设备
     * 
     * @param duidList
     * @return
     */
    private boolean checkDeviceOnline(List<TaskDeviceInfo> duidList) {
        return CollectionUtils.isEmpty(mDeviceInfoMapper.selectByDuids(duidList.stream().map(TaskDeviceInfo::getDuid).collect(Collectors.toList()), 0));
    }

    @Override
    public void exportPdf(Integer taskId, HttpServletResponse response) {
        // 根据 taskId 查询已执行任务列表
        List<TaskDTO> taskDTOs = taskMapper.queryByProjectId(taskId);
        if (CollectionUtils.isEmpty(taskDTOs)) {
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }

        TaskDTO lastTaskHistoryByTaskId = taskDTOs.get(0);
        EmailReportDTO emailReportDTO = taskManager.queryAndBuildHashMapForEmail(lastTaskHistoryByTaskId.getId());

        File pdfFile = null;
        OutputStream out = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            User loginUser = (User) SecurityUtils.getSubject().getPrincipal();
            pdfFile = PdfProjectTestReport.generateFile(emailReportDTO, url + "/" + buckName + "/",
                    true, StringUtils.join(loginUser.getUsername(), DateUtil.parseDateToStr(new Date(), DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISS)));
            String fileName = "测试报告-" + taskId + ".pdf";
            response.setContentType("application/msexcel;charset=UTF-8");
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
            out = response.getOutputStream();

            byte[] buffer = new byte[1024];
            fis = new FileInputStream(pdfFile);
            bis = new BufferedInputStream(fis);
            out = response.getOutputStream();
            int i = bis.read(buffer);
            while (i != -1) {
                out.write(buffer, 0, i);
                i = bis.read(buffer);
            }
        } catch (Exception e) {
            logger.error("export test report pdf error. message:{}", e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    logger.error("close outputStream error. message:{}", e.getMessage());
                }
            }
            if (pdfFile != null) {
                pdfFile.delete();
            }
        
        }
    }


    @Value("${export.temp.file-path}")
    private String tempFilePath;

    @Autowired
    private TaskDeviceMemoryLogMapper taskDeviceMemoryLogMapper;

    @Autowired
    private TaskDeviceTopLogMapper taskDeviceTopLogMapper;

    @Autowired
    private TestReportMapper testReportMapper;

    @Autowired
    private TaskReportFileMapper taskReportFileMapper;

    @Autowired
    private MinioUtils minioUtils;

    @Override
    public void exportMemoryReport(Integer taskId, HttpServletResponse response) {
        // 根据 taskId 查询已执行任务列表
        List<TaskDTO> taskDTOs = taskMapper.queryByProjectId(taskId);
        if (CollectionUtils.isEmpty(taskDTOs)) {
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }
        File excelFile = null;
        OutputStream out = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            TaskReportFileExample example = new TaskReportFileExample();
            example.createCriteria().andTaskIdEqualTo(taskId.longValue()).andFileBusinessTypeEqualTo("02");
            List<TaskReportFile> taskReportFiles = taskReportFileMapper.selectByExample(example);
            if(CollectionUtils.isNotEmpty(taskReportFiles)){
                TaskReportFile taskReportFile = taskReportFiles.get(0);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(taskReportFile.getFileName(), "UTF-8"));
                try {
                    InputStream objectInputStream = minioUtils.getObjectInputStream(taskReportFile.getFileId());
                    IOUtils.copy(objectInputStream, response.getOutputStream());
                    response.flushBuffer();
                    objectInputStream.close();
                } catch (Exception e) {
                    logger.error("exportMemoryReport InputStream error",e);
                }
            }else {
                ExcelDataVo excelData = getExcelDataVoForMemory(taskId);
                //String fileName = "task-" + taskId + "-memory-report.xlsx";
                String fileName = "memory.xlsx";
                excelFile = ExcelUtil.generateExcelFileByTemplate2("memory-report-template.xlsx", tempFilePath, fileName, excelData, ReportMemoryRecord.class,1);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
                out = response.getOutputStream();

                byte[] buffer = new byte[1024];
                fis = new FileInputStream(excelFile);
                bis = new BufferedInputStream(fis);
                out = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    out.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
                TaskDTO task = taskMapper.getTaskById(taskId);
                saveExcelFile(task, excelFile, fileName,"02");
            }
        } catch (Exception e) {
            logger.error("export test report pdf error. message:{}", e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    logger.error("close outputStream error. message:{}", e.getMessage());
                }
            }
            if (excelFile != null) {
                excelFile.delete();
            }
        }
    }

    public ExcelDataVo getExcelDataVoForMemory(Integer taskId) {
        TaskDeviceMemoryLogExample example = new TaskDeviceMemoryLogExample();
        example.createCriteria().andTaskIdEqualTo(Long.valueOf(taskId)).andIsDeletedEqualTo(0);
        example.setOrderByClause(" name ");
        List<TaskDeviceMemoryLog> taskDeviceMemoryLogs = taskDeviceMemoryLogMapper.selectByExample(example);
        ExcelDataVo excelData =  new ExcelDataVo();
        ExcelDataVo.StatisticSheetData sheetDataMemory = buildHmiMemorySheetData(taskId);
        excelData.setSheetData(sheetDataMemory);
        if(CollectionUtils.isEmpty(taskDeviceMemoryLogs)){
            ExcelDataVo.SheetData<ReportMemoryRecord> sheetData1 = new ExcelDataVo.SheetData();
            sheetData1.setSheetName("no-data");
            excelData.getSheetDataList().add(sheetData1);
        }else{
            HashMap<String, ExcelDataVo.SheetData<ReportMemoryRecord>> tempMap = new HashMap<>();
            for(TaskDeviceMemoryLog memoryLog : taskDeviceMemoryLogs){
                ExcelDataVo.SheetData<ReportMemoryRecord> sheetData = tempMap.get(memoryLog.getName());
                if(sheetData == null){
                    sheetData = new ExcelDataVo.SheetData<ReportMemoryRecord>();
                    sheetData.setSheetName(memoryLog.getName());
                    tempMap.put(memoryLog.getName(), sheetData);
                    excelData.getSheetDataList().add(sheetData);
                }
                ReportMemoryRecord record = JSONObject.parseObject(JSONObject.toJSONString(memoryLog), ReportMemoryRecord.class);
                sheetData.getReportRecords().add(record);
            }
            logger.info("excelData:{}", JSONObject.toJSONString(excelData));
        }
        return excelData;
    }

    @Override
    public void exportTopReport(Integer taskId, HttpServletResponse response) {
        // 根据 taskId 查询已执行任务列表
        List<TaskDTO> taskDTOs = taskMapper.queryByProjectId(taskId);
        if (CollectionUtils.isEmpty(taskDTOs)) {
            throw new CustomException(ResultCode.DATA_IS_WRONG);
        }
        File excelFile = null;
        OutputStream out = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            TaskReportFileExample example = new TaskReportFileExample();
            example.createCriteria().andTaskIdEqualTo(taskId.longValue()).andFileBusinessTypeEqualTo("01");
            List<TaskReportFile> taskReportFiles = taskReportFileMapper.selectByExample(example);
            if(CollectionUtils.isNotEmpty(taskReportFiles)){
                TaskReportFile taskReportFile = taskReportFiles.get(0);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(taskReportFile.getFileName(), "UTF-8"));
                try {
                    InputStream objectInputStream = minioUtils.getObjectInputStream(taskReportFile.getFileId());
                    IOUtils.copy(objectInputStream, response.getOutputStream());
                    response.flushBuffer();
                    objectInputStream.close();
                } catch (Exception e) {
                    logger.error("exportTopReport InputStream error",e);
                }
            }else {
                ExcelDataVo excelData = getExcelDataVoForTop(taskId);
                //String fileName = "task-" + taskId + "-top-report.xlsx";
                String fileName = "top.xlsx";
                excelFile = ExcelUtil.generateExcelFileByTemplate2("top-report-template.xlsx", tempFilePath, fileName, excelData, ReportTopRecord.class,1);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
                out = response.getOutputStream();

                byte[] buffer = new byte[1024];
                fis = new FileInputStream(excelFile);
                bis = new BufferedInputStream(fis);
                out = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    out.write(buffer, 0, i);
                    i = bis.read(buffer);
                }
                TaskDTO task = taskMapper.getTaskById(taskId);
                saveExcelFile(task, excelFile, fileName,"01");
            }
        } catch (Exception e) {
            logger.error("export test report pdf error. message:{}", e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    logger.error("close outputStream error. message:{}", e.getMessage());
                }
            }
            if (excelFile != null) {
                excelFile.delete();
            }
        }
    }

    public ExcelDataVo getExcelDataVoForTop(Integer taskId) {
        TaskDeviceTopLogExample example = new TaskDeviceTopLogExample();
        example.createCriteria().andTaskIdEqualTo(Long.valueOf(taskId)).andIsDeletedEqualTo(0);
        example.setOrderByClause(" name ");
        List<TaskDeviceTopLog> taskDeviceMemoryLogs = taskDeviceTopLogMapper.selectByExample(example);
        ExcelDataVo excelData =  new ExcelDataVo();
        ExcelDataVo.StatisticSheetData sheetDataTop = buildHmiCpuSheetData(taskId);;
        excelData.setSheetData(sheetDataTop);
        if(CollectionUtils.isEmpty(taskDeviceMemoryLogs)){
            ExcelDataVo.SheetData<ReportMemoryRecord> sheetData1 = new ExcelDataVo.SheetData();
            sheetData1.setSheetName("no-data");
        }else{
            HashMap<String, ExcelDataVo.SheetData<ReportTopRecord>> tempMap = new HashMap<>();
            for(TaskDeviceTopLog log : taskDeviceMemoryLogs){
                ExcelDataVo.SheetData<ReportTopRecord> sheetData = tempMap.get(log.getName());
                if(sheetData == null){
                    sheetData = new ExcelDataVo.SheetData<ReportTopRecord>();
                    sheetData.setSheetName(log.getName());
                    tempMap.put(log.getName(), sheetData);
                    excelData.getSheetDataList().add(sheetData);
                }
                ReportTopRecord record = JSONObject.parseObject(JSONObject.toJSONString(log), ReportTopRecord.class);
                BigDecimal bigDecimal = new BigDecimal(log.getCpu());
                BigDecimal bigDecima2 = new BigDecimal(log.getMem());
                record.setCpu(String.valueOf(bigDecimal.divide(new BigDecimal(10)).doubleValue()));
                record.setMem(String.valueOf(bigDecima2.divide(new BigDecimal(10)).doubleValue()));
                sheetData.getReportRecords().add(record);
            }
            logger.info("excelData:{}", JSONObject.toJSONString(excelData));
        }
        return excelData;
    }

//    @Autowired
//    private TaskColdHotStartupLogMapper taskColdHotStartupLogMapper;

    @Override
    public void exportHmiReport(ExcelReportRequest reportRequest,HttpServletResponse response) {
//        TaskSearchDTO taskSearchDTO = new TaskSearchDTO();
//        taskSearchDTO.setId(reportRequest.getTaskId());
//        List<TaskDTO> taskDTOs = taskMapper.getMonkeyTaskList(taskSearchDTO);
//        if (CollectionUtils.isEmpty(taskDTOs)) {
//            throw new CustomException(ResultCode.DATA_IS_WRONG);
//        }
        TaskDTO task = taskMapper.getTaskById(reportRequest.getTaskId());
        File excelFile = null;
        OutputStream out = null;
        FileInputStream fis = null;
        BufferedInputStream bis = null;
        try {
            TaskReportFileExample example = new TaskReportFileExample();
            example.createCriteria().andTaskIdEqualTo(reportRequest.getTaskId().longValue()).andFileBusinessTypeEqualTo("03");
            List<TaskReportFile> taskReportFiles = taskReportFileMapper.selectByExample(example);
            if(CollectionUtils.isNotEmpty(taskReportFiles)){
                TaskReportFile taskReportFile = taskReportFiles.get(0);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(taskReportFile.getFileName(), "UTF-8"));
                try {
                    InputStream objectInputStream = minioUtils.getObjectInputStream(taskReportFile.getFileId());
                    IOUtils.copy(objectInputStream, response.getOutputStream());
                    response.flushBuffer();
                    objectInputStream.close();
                } catch (Exception e) {
                    logger.error("exportHmiReport InputStream error",e);
                }
            }else {
                ExcelDataVo excelData = getExcelDataVoForHMI(task);

                //String fileName = "tasks-HMI-report.xlsx";
                String fileName = "HMI性能分析.xlsx";
                excelFile = ExcelUtil.generateExcelFileByTemplate2("hmi-report-template.xlsx", tempFilePath, fileName, excelData, ColdHotStartupRecord.class, 1);
                response.setContentType("application/msexcel;charset=UTF-8");
                response.setHeader("Content-Disposition",
                        "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8"));
                out = response.getOutputStream();

                byte[] buffer = new byte[1024];
                fis = new FileInputStream(excelFile);
                bis = new BufferedInputStream(fis);
                out = response.getOutputStream();
                int i = bis.read(buffer);
                while (i != -1) {
                    out.write(buffer, 0, i);
                    i = bis.read(buffer);
                }

                saveExcelFile(task, excelFile, fileName,"03");
            }
        } catch (Exception e) {
            logger.error("export test report pdf error. message:{}", e.getMessage());
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    logger.error("close outputStream error. message:{}", e.getMessage());
                }
            }
            if (excelFile != null) {
                excelFile.delete();
            }
        }
    }

    public ExcelDataVo getExcelDataVoForHMI(TaskDTO task) {
        ExcelDataVo excelData =  new ExcelDataVo();
//            ExcelStatisticDataVo.SheetData<ReportHmiMemoryRecord> sheetData1 = buildHmiMemorySheetData(task.getId());
//            ExcelStatisticDataVo.SheetData<ReportHmiCpuRecord> sheetData2 = buildHmiCpuSheetData(task.getId());;
        ExcelDataVo.StatisticSheetData<ReportHmiStartRecord> sheetData3 = buildHmiStartSheetData(task);
        List<ExcelDataVo.SheetData> sheetDataList = buildHmiColdStartDetailSheet(task);
//            excelData.getSheetDataList().add(sheetData1);
//            excelData.getSheetDataList().add(sheetData2);
        excelData.setSheetData(sheetData3);
        excelData.setSheetDataList(sheetDataList);
        return excelData;
    }

    ExcelDataVo.StatisticSheetData buildHmiMemorySheetData(Integer taskId){
        ExcelDataVo.StatisticSheetData sheetData = new ExcelDataVo.StatisticSheetData();
        sheetData.setSheetName("内存");
//        sheetData.setRecordClass(ReportHmiMemoryRecord.class);
        List<ReportHmiMemoryInfo> reportHmiMemoryInfos = taskDeviceMemoryLogMapper.selectStatisticByTaskId(Long.valueOf(taskId));
        if(CollectionUtils.isEmpty(reportHmiMemoryInfos)){
            return sheetData;
        }
        for(ReportHmiMemoryInfo reportHmiMemoryInfo:reportHmiMemoryInfos){
            ReportHmiMemoryRecord record = new ReportHmiMemoryRecord();
            BeanUtils.copyProperties(reportHmiMemoryInfo,record);
            record.setPackageName(reportHmiMemoryInfo.getPackageName());
            sheetData.getReportRecords().add(record);
        }
        logger.info("HmiMemory sheetData: {}", JSONObject.toJSONString(sheetData.getReportRecords()));
        return sheetData;
    }

    ExcelDataVo.StatisticSheetData  buildHmiCpuSheetData(Integer taskId){
        ExcelDataVo.StatisticSheetData<ReportHmiCpuRecord> sheetData = new ExcelDataVo.StatisticSheetData<>();
        sheetData.setSheetName("cpu");
        //sheetData.setRecordClass(ReportHmiCpuRecord.class);
        List<ReportHmiCpuInfo> reportHmiCpuInfos = taskDeviceTopLogMapper.selectStatisticByTaskId(Long.valueOf(taskId));
        if(CollectionUtils.isEmpty(reportHmiCpuInfos)){
            return sheetData;
        }
        for(ReportHmiCpuInfo reportHmiCpuInfo : reportHmiCpuInfos){
            ReportHmiCpuRecord record = new ReportHmiCpuRecord();
            BeanUtils.copyProperties(reportHmiCpuInfo, record);
            record.setPackageName(reportHmiCpuInfo.getPackageName());
            record.setCpuMax(reportHmiCpuInfo.getCpuMax());
            record.setCpuAvg(reportHmiCpuInfo.getCpuAvg());
            record.setCpuMin(reportHmiCpuInfo.getCpuMin());
            sheetData.getReportRecords().add(record);
        }
        logger.info("HmiCpu sheetData: {}", JSONObject.toJSONString(reportHmiCpuInfos));
        logger.info("HmiCpu sheetData: {}", JSONObject.toJSONString(sheetData.getReportRecords()));

        return sheetData;
    }

    public List<ExcelDataVo.SheetData> buildHmiColdStartDetailSheet(TaskDTO task){
        List<ExcelDataVo.SheetData> detailSheetList = new ArrayList<>();
        TaskColdHotStartupLogExample example = new TaskColdHotStartupLogExample();
        example.createCriteria().andTaskIdEqualTo(task.getId());
        example.setOrderByClause(" package_name ");
        List<TaskColdHotStartupLog> taskColdHotStartupLogs = taskColdHotStartupLogMapper.selectByExample(example);
        if(CollectionUtils.isEmpty(taskColdHotStartupLogs)){
            ExcelDataVo.SheetData<ReportMemoryRecord> sheetData1 = new ExcelDataVo.SheetData();
            sheetData1.setSheetName("no-data");
            detailSheetList.add(sheetData1);
            return detailSheetList;
        }
        HashMap<String, ExcelDataVo.SheetData<ColdHotStartupRecord>> tempMap = new HashMap<>();
        for(TaskColdHotStartupLog log : taskColdHotStartupLogs){
            ExcelDataVo.SheetData<ColdHotStartupRecord> sheet = tempMap.get(log.getPackageName());
            if(sheet == null){
                sheet = new ExcelDataVo.SheetData<>();
                sheet.setSheetName(log.getPackageName());
                tempMap.put(log.getPackageName(), sheet);
                detailSheetList.add(sheet);
            }
            ColdHotStartupRecord record = new ColdHotStartupRecord();
            record.setPackageName(log.getPackageName());
            record.setSpendTime(log.getStartupResponseTime());
            record.setType(log.getType()==1?"冷启":"热启");
            sheet.getReportRecords().add(record);
        }
        return detailSheetList;
    }


    public ExcelDataVo.StatisticSheetData<ReportHmiStartRecord> buildHmiStartSheetData(TaskDTO task){
        ExcelDataVo.StatisticSheetData<ReportHmiStartRecord> sheetData = new ExcelDataVo.StatisticSheetData<>();
        sheetData.setSheetName("冷热启动汇总数据");
        //sheetData.setStatisticSheetData(ReportHmiStartRecord.class);

//        List<TestReport> trList = testReportMapper.select(testReportSearchDTO);
//        List<String> ids = trList.stream().map(TestReport::getId).distinct().collect(Collectors.toList());
//        String[] strings = task.getPackages().split(",");
//        List<String> packageNames = Arrays.asList(strings);
//        TestReportApplicationResponseTimeExample example = new TestReportApplicationResponseTimeExample();
//        example.createCriteria().andPackageNameIn(packageNames);
//        example.setOrderByClause(" package_name, type ");
        List<ExcelStartupInfo> excelStartupInfos = taskColdHotStartupLogMapper.selectStatisticForVarianceByTaskId(task.getId());
        if(CollectionUtils.isEmpty(excelStartupInfos)){
            return sheetData;
        }
        logger.info("HmiStart excelStartupInfos: {}", JSONObject.toJSONString(excelStartupInfos));
        HashMap<String, ReportHmiStartRecord> tmpMap = new HashMap<>();
        for(ExcelStartupInfo excelStartupInfo : excelStartupInfos){
            ReportHmiStartRecord startRecord = tmpMap.get(excelStartupInfo.getPackageName());
            if(startRecord == null){
                startRecord = new ReportHmiStartRecord();
                sheetData.getReportRecords().add(startRecord);
                startRecord.setPackageName(excelStartupInfo.getPackageName());
                startRecord.setWarmStart("N/A");
                tmpMap.put(excelStartupInfo.getPackageName(), startRecord);
            }
            if(excelStartupInfo.getType().equals(1)){
                startRecord.setColdStart(excelStartupInfo.getStartupAvg()+"/"+excelStartupInfo.getVariance());
            }else if(excelStartupInfo.getType().equals(2)){
                startRecord.setHotStart(excelStartupInfo.getStartupAvg()+"/"+excelStartupInfo.getVariance());
            }
        }
        logger.info("HmiStart sheetData: {}", JSONObject.toJSONString(sheetData.getReportRecords()));
        return sheetData;
    }


    /**
     * 延时任务需要更新父任务的状态
     * 
     * @param parentId
     */
    private void updateStatusIfNotImmediateTask(Integer parentId) {
        if(parentId == null) {
            return;
        }

        TaskDTO taskDTO = taskMapper.getTaskById(parentId);
        if(null == taskDTO || taskDTO.getCycleType().intValue() == 1) {
            return;
        }
        if(taskDTO.getCycleType().intValue() == 0) {
            CronExpression cronExpression;
            try {
                cronExpression = new CronExpression(taskDTO.getCron());
                LocalDateTime execTime = com.pactera.asmp.server.utils.DateUtil.toLocalDateTime(cronExpression.getNextValidTimeAfter(com.pactera.asmp.server.utils.DateUtil.toDate(LocalDateTime.now())));
                logger.info("execTime:{}, exeEndTime:{}", execTime.toString(), taskDTO.getExeEndTime().toString());
                if(!execTime.isAfter(taskDTO.getExeEndTime())) {
                    return;
                }
            } catch (ParseException e) {
                logger.error("获取下次定时任务时间报错。message:{}", e.getMessage());
                return;
            }
        }
        taskDTO.setStatus(TaskStatus.TASK_COMPLETE.getValue());
        taskMapper.update(taskDTO);

        List<TaskDTO> listByParentid = taskMapper.getTaskHistListByParentid(new TaskSearchDTO() {{setParentId(parentId);}});
        for(TaskDTO histTask: listByParentid) {
            histTask.setStatus(TaskStatus.TASK_COMPLETE.getValue());
            taskMapper.updateHistory(histTask);
        }
    }

    @Override
    public ResponseResult createAutoTestTask(AutoTestTaskExecDTO autoTestTaskExecDTO) throws Exception {
        ProductionLineHistory productionLineHistory = productionLineHistoryMapper.selectByPrimaryKey(autoTestTaskExecDTO.getProductionLineHistoryId());
        if(null == productionLineHistory || StringUtils.isEmpty(productionLineHistory.getScriptIds())) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_PRODUCTION_LINE_NOT_EXISTED);
        }
        if(null == productionLineHistory.getSubscribeStatus() || productionLineHistory.getSubscribeStatus().intValue() != 1) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_PRODUCTION_LINE_NOT_SUBSCRIBE);
        }
        List<DeviceInfo> deviceInfos = mDeviceInfoMapper.selectByMasterComputerIds(Lists.newArrayList(autoTestTaskExecDTO.getMasterComputerId()), null);
        if(CollectionUtils.isEmpty(deviceInfos)) {
            throw new CustomException(ResultCode.DEVICE_NOTEXIST_OR_OFFLINE);
        }

        // 获取项目名称以及模块名称
        ProjectInfo projectInfo = projectMapper.selectByPrimaryKey(productionLineHistory.getProjectId());
        if(null == projectInfo) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_NOT_FIND_ERR);
        }
        List<ProjectModuleDTO> projectModuleDTOs = projectModuleMapper.selectByIds(Lists.newArrayList(productionLineHistory.getProjectModuleId()));
        if(CollectionUtils.isEmpty(projectModuleDTOs)) {
            throw new CustomException(ResultCode.BUSINESS_PROJECT_MODULE_NOT_EXISTED);
        }
        List<ScriptInfo2> scriptInfos = iScriptInfoServcie.selectScriptByScriptIds(Arrays.stream(productionLineHistory.getScriptIds().split(","))
                .map(s -> Integer.parseInt(s.trim()))
                .collect(Collectors.toList()));
        List<TaskDeviceInfo> duidList = new ArrayList<>();
        TaskDeviceInfo taskDeviceInfo = new TaskDeviceInfo();
        Optional<DeviceInfo> optional = deviceInfos.stream().filter(x -> x.getType() == null && !x.getAuxiliary() && StringUtils.isNotEmpty(x.getDisplayIdsStr())).findFirst();
        if(optional.isPresent()) {
            taskDeviceInfo.setDuid(optional.get().getDuid());
            // 判断是否需要辅助设备
            if(scriptInfos.stream().anyMatch(x -> x.getSupportAuxiliary().intValue() == 1)) {
                Optional<DeviceInfo> auxOptional = deviceInfos.stream().filter(x -> x.getAuxiliary()).findFirst();
                if(!auxOptional.isPresent()) {
                    throw new CustomException(ResultCode.BUSINESS_PROJECT_PRODUCTION_LINE_DEVICE_NOT_EXISTED);
                }
                taskDeviceInfo.setAuxDuid(auxOptional.get().getDuid());
            }
        }else {
            Optional<DeviceInfo> auxOptional = deviceInfos.stream().filter(x -> x.getAuxiliary()).findFirst();
            if(!auxOptional.isPresent()) {
                throw new CustomException(ResultCode.BUSINESS_PROJECT_PRODUCTION_LINE_DEVICE_NOT_EXISTED);
            }
            taskDeviceInfo.setDuid(auxOptional.get().getDuid());
        }
        duidList.add(taskDeviceInfo);

        Date date = new Date();
        TaskDTO taskDTO = new TaskDTO();
        taskDTO.setProjectId(productionLineHistory.getProjectId());
        taskDTO.setName(StringUtils.join(projectInfo.getProjectName(), "_",
                projectModuleDTOs.get(0).getName(), "_", autoTestTaskExecDTO.getApkName(), "_", DateUtil.parseDateToStr(date, DateUtil.DATE_TIME_FORMAT_YYYYMMDDHHMISS)));
        taskDTO.setType(2);
        taskDTO.setCycleType(2);
        taskDTO.setCron(null);
        taskDTO.setScriptIds(productionLineHistory.getScriptIds());
        taskDTO.setDuid(optional.get().getDuid());
        taskDTO.setExecType(1);
        taskDTO.setExeNumber(1);
        taskDTO.setProductionLineHistoryId(productionLineHistory.getId());
        taskDTO.setExeStartTime(DateUtil.toLocalDateTime(date));
        taskDTO.setExeEndTime(DateUtil.toLocalDateTime(date));

        TaskCreateRequest taskCreateRequest = new TaskCreateRequest();
        taskCreateRequest.setTaskDTO(taskDTO);
        taskCreateRequest.setDuidList(duidList);

        return this.newTaskSchedule(taskCreateRequest);
    }

    @Override
    public ResponseResult reportPerformance(TerminalPerformanceInfo tpInfo) {
        AssertUtil.notNull(tpInfo.getTaskId(), TASKID_NOTNULL);
        AssertUtil.notNull(tpInfo.getDuid(), DUID_NOTNULL);
        Subtask subtask = subtaskMapper.selectByPrimaryKey(tpInfo.getTaskId());
        if(subtask == null){
            return ResponseResult.defaultFail(ResultCode.BUSINESS_TASK_NOT_FIND_ERR,null);
        }
        TaskDTO taskHistory = taskMapper.getTaskHistoryById(subtask.getTaskId().intValue());
        if(CollectionUtils.isNotEmpty(tpInfo.getMemoryList())) {
            for(TerminalPerformanceInfo.memoryInfo memoryInfo : tpInfo.getMemoryList()) {
                TaskDeviceMemoryLog record = new TaskDeviceMemoryLog();
                BeanUtils.copyProperties(memoryInfo,record);
                record.setTaskId(taskHistory.getParentId().longValue());
                record.setSubTaskId(subtask.getId());
                record.setDuid(tpInfo.getDuid());
                record.setCreateTime(new Date());
                record.setTimeStamp(memoryInfo.getTimestamp());
                taskDeviceMemoryLogMapper.insertSelective(record);
            }
        }
        if(CollectionUtils.isNotEmpty(tpInfo.getTopList())){
            for(TerminalPerformanceInfo.TopInfo topInfo : tpInfo.getTopList()) {
                TaskDeviceTopLog record = new TaskDeviceTopLog();
                //BeanUtils.copyProperties(topInfo,record);
                record.setTaskId(taskHistory.getParentId().longValue());
                record.setSubTaskId(subtask.getId());
                record.setDuid(tpInfo.getDuid());
                record.setName(topInfo.getName());
                record.setUser(topInfo.getUser());
                record.setPid(topInfo.getPid());
                record.setRes(topInfo.getRes());
                record.setS(topInfo.getS());
                record.setVirt(topInfo.getVirt());
                record.setShrV(topInfo.getShr());
                record.setCreateTime(new Date());
                record.setTimeStamp(topInfo.getTimestamp());
                record.setTimePlus(topInfo.getTimePlus());
                record.setPr(topInfo.getPr());
                record.setNi(topInfo.getNi());
                double cpu = Double.parseDouble(topInfo.getCpu())*10;
                record.setCpu(Double.valueOf(cpu).intValue());
                double mem = Double.parseDouble(topInfo.getMem())*10;
                record.setMem(Double.valueOf(mem).intValue());

                taskDeviceTopLogMapper.insertSelective(record);
            }
        }
        return ResponseResult.defaultSuccess(null);
    }

    @Override
    public ResponseResult queryPackageRecord(PackageRecord packageRecord) {
        QueryPackageRecords queryPackageRecords = new QueryPackageRecords();
        TaskPackageInfoExample taskPackageInfoExample = new TaskPackageInfoExample();
        taskPackageInfoExample.createCriteria().andIsDeletedEqualTo(0).andProjectIdEqualTo(packageRecord.getProjectId());
        taskPackageInfoExample.setOrderByClause(" package_name ");
        List<TaskPackageInfo> taskPackageInfos = taskPackageInfoMapper.selectByExample(taskPackageInfoExample);
        if(CollectionUtils.isNotEmpty(taskPackageInfos)){
           for(TaskPackageInfo packageInfo: taskPackageInfos){
               PackageRecord pr = new PackageRecord();
               pr.setPackageName(packageInfo.getPackageName());
               pr.setActivity(packageInfo.getActivity());
               pr.setProjectId(packageInfo.getProjectId());
               queryPackageRecords.getList().add(pr);
           }
        }
        return ResponseResult.defaultSuccess(queryPackageRecords);
    }

    @Override
    public ResponseResult delPackageRecord(PackageRecord packageRecord) {
        TaskPackageInfoExample taskPackageInfoExample = new TaskPackageInfoExample();
        taskPackageInfoExample.createCriteria().
                andPackageNameEqualTo(packageRecord.getPackageName()).andActivityEqualTo(packageRecord.getActivity()).andIsDeletedEqualTo(0);
        List<TaskPackageInfo> taskPackageInfos = taskPackageInfoMapper.selectByExample(taskPackageInfoExample);
        if(CollectionUtils.isEmpty(taskPackageInfos)){
            return ResponseResult.defaultSuccess(null);
        }
        TaskPackageInfo record = taskPackageInfos.get(0);
//        record.setIsDeleted(1);
//        taskPackageInfoMapper.updateByPrimaryKeySelective(record);
        taskPackageInfoMapper.deleteByPrimaryKey(record.getId());
        return ResponseResult.defaultSuccess(null);
    }

    @Override
    public ResponseResult addPackageRecord(PackageRecord packageRecord) {
        TaskPackageInfo record = new TaskPackageInfo();
        record.setProjectId(packageRecord.getProjectId());
        record.setPackageName(packageRecord.getPackageName());
        record.setActivity(packageRecord.getActivity());
        record.setCreateTime(new Date());
        try {
            taskPackageInfoMapper.insertSelective(record);
        }catch (DuplicateKeyException e){
            logger.error(" 包名重复:{}",JSONObject.toJSONString(record));
            return ResponseResult.defaultFail(ResultCode.FAIL,"包名重复!");
        }
        return ResponseResult.defaultSuccess(null);
    }

//    @Override
//    public ResponseResult getReportPerformanceInfo(Integer taskId) {
//        return null;
//    }

    /**
     * 处理性能测试结果报告
     * 
     * @param testReportId
     * @param dto
     */
    private void handleTestedReport(Integer testReportId, TestReportDTO dto) {
        for (TestReportApplicationResponseTimeDTO testReportApplicationResponseTimeDTO : dto.getTestedReport()) {
            if(null == testReportApplicationResponseTimeDTO.getProjectName()) {
                logger.error("TestReportApplicationResponseTime error.TestReportId:{}, dto:{}", testReportId
                        , JSONObject.toJSONString(testReportApplicationResponseTimeDTO));
                continue;
            }
            TestReportApplicationResponseTime testReportApplicationResponseTime = new TestReportApplicationResponseTime();
            testReportApplicationResponseTime.setTestReportId(testReportId);
            testReportApplicationResponseTime.setScriptId(dto.getScriptId());
            testReportApplicationResponseTime.setType(testReportApplicationResponseTimeDTO.getProjectName().getCode());
            testReportApplicationResponseTime.setPackageName(testReportApplicationResponseTimeDTO.getPackageName());
            testReportApplicationResponseTime.setActivityName(testReportApplicationResponseTimeDTO.getActivityName());
            if(null != testReportApplicationResponseTimeDTO.getTimeRange()) {
                testReportApplicationResponseTime.setStartTime(testReportApplicationResponseTimeDTO.getTimeRange().getStartTime());
                testReportApplicationResponseTime.setEndTime(testReportApplicationResponseTimeDTO.getTimeRange().getEndTime());
            }
            testReportApplicationResponseTime.setTestTimes(testReportApplicationResponseTimeDTO.getTestTimes());
            // 启动指令
            if(null != testReportApplicationResponseTimeDTO.getStartupResponseTime()) {
                testReportApplicationResponseTime.setStartupResponseTimeMean(testReportApplicationResponseTimeDTO.getStartupResponseTime().getMeanTime());
                testReportApplicationResponseTime.setStartupResponseTimeMax(testReportApplicationResponseTimeDTO.getStartupResponseTime().getMaxTime());
                testReportApplicationResponseTime.setStartupResponseTimeMin(testReportApplicationResponseTimeDTO.getStartupResponseTime().getMinTime());
            }
            // 启动画面
            if(null != testReportApplicationResponseTimeDTO.getStartupChangeTime()) {
                testReportApplicationResponseTime.setStartupChangeTimeMean(testReportApplicationResponseTimeDTO.getStartupChangeTime().getMeanTime());
                testReportApplicationResponseTime.setStartupChangeTimeMax(testReportApplicationResponseTimeDTO.getStartupChangeTime().getMaxTime());
                testReportApplicationResponseTime.setStartupChangeTimeMin(testReportApplicationResponseTimeDTO.getStartupChangeTime().getMinTime());
            }
            // 退出指令
            if(null != testReportApplicationResponseTimeDTO.getCloseResponseTime()) {
                testReportApplicationResponseTime.setCloseResponseTimeMean(testReportApplicationResponseTimeDTO.getCloseResponseTime().getMeanTime());
                testReportApplicationResponseTime.setCloseResponseTimeMax(testReportApplicationResponseTimeDTO.getCloseResponseTime().getMaxTime());
                testReportApplicationResponseTime.setCloseResponseTimeMin(testReportApplicationResponseTimeDTO.getCloseResponseTime().getMinTime());
            }
            // 退出画面
            if(null != testReportApplicationResponseTimeDTO.getCloseChangeTime()) {
                testReportApplicationResponseTime.setCloseChangeTimeMean(testReportApplicationResponseTimeDTO.getCloseChangeTime().getMeanTime());
                testReportApplicationResponseTime.setCloseChangeTimeMax(testReportApplicationResponseTimeDTO.getCloseChangeTime().getMaxTime());
                testReportApplicationResponseTime.setCloseChangeTimeMin(testReportApplicationResponseTimeDTO.getCloseChangeTime().getMinTime());
            }
            testReportApplicationResponseTime.setCreateTime(new Date());
            testReportApplicationResponseTimeMapper.insert(testReportApplicationResponseTime);
        }
    }

    private String getStepContent(List<ScriptInfo2> scriptInfos, Integer scriptId) {
        String stepContent = "";
        for(ScriptInfo2 scriptInfo2: scriptInfos) {
            stepContent += scriptInfo2.getName() + "<br/>";
            if(scriptInfo2.getScriptData().get("performanceInfos") != null) {
                List<Map<String, Object>> list = JSONObject.parseObject(JSONObject.toJSONString(scriptInfo2.getScriptData().get("performanceInfos")),
                        new TypeReference<List<Map<String, Object>>> () {}) ;
                for(Map<String, Object> map:list) {
                    if(map.get("title") != null && StringUtils.isNotEmpty(map.get("title").toString())) {
                        stepContent += "&nbsp;&nbsp;&nbsp;&nbsp;" + map.get("title").toString() + "<br/>";
                    }else {
                        stepContent += "&nbsp;&nbsp;&nbsp;&nbsp;" + String.valueOf(map.get("type")) + "<br/>";
                    }
                }
            }else {
                List<Map<String, Object>> list = JSONObject.parseObject(JSONObject.toJSONString(scriptInfo2.getScriptData().get("action")),
                        new TypeReference<List<Map<String, Object>>> () {}) ;
                for(Map<String, Object> map:list) {
                    if(map.get("title") != null && StringUtils.isNotEmpty(map.get("title").toString())) {
                        stepContent += "&nbsp;&nbsp;&nbsp;&nbsp;" + map.get("title").toString() + "<br/>";
                    }else {
                        stepContent += "&nbsp;&nbsp;&nbsp;&nbsp;" + String.valueOf(map.get("type")) + "<br/>";
                    }
                }
            }
            if(scriptInfo2.getId().intValue() == scriptId.intValue()) {
                break;
            }
        }
        return stepContent;
    }

    private void saveExcelFile(TaskDTO task, File excelFile1, String fileName1,String fileBusinessType) {
        String fileId ="";
        FileInputStream fileInputStream = null;
        try {
            fileInputStream = new FileInputStream(excelFile1);
            fileId = minioUtils.uploadFile(fileInputStream, fileName1,null );
            TaskReportFile record = new TaskReportFile();
            record.setCreateTime(new Date());
            record.setFileId(fileId);
            record.setFileBusinessType(fileBusinessType);
            record.setFileName(fileName1);
            record.setTaskId(task.getId().longValue());
            taskReportFileMapper.insertSelective(record);
        } catch (Exception e) {
            logger.error("excel file upload error: ", e);
            throw new CustomException(ResultCode.UPLOAD_ERROR);
        }finally {
            if(fileInputStream!=null){
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    logger.error("excel file close error: ", e);
                }
            }
        }

    }
}
